/*
 * com_e4graph_Storage.cpp --
 *
 *	This file contains implementations of the natives of the
 *	class com.e4graph.Storage.
 *
 * NOTE: At the end of this file, there is one native for the class
 * com_e4graph_StorageIterator. It is in this file so that it can use
 * the file-static cache data structures.
 *
 * 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"

#define STATIC_STORAGE_SIZE	128
typedef struct StorageCacheRecord {
    e4_Storage s;
    int generation;
} StorageCacheRecord;

static StorageCacheRecord staticStorages[STATIC_STORAGE_SIZE];
static int storageSize = STATIC_STORAGE_SIZE;

StorageCacheRecord *storages = staticStorages;
static int generation = 0;

/*
 * Object used to lock storage cache.
 */

jobject		storageLock = NULL;

/*
 * These classes represent exception types.
 */

jclass		clsStorageCreationException = NULL;
jclass		clsStorageIsNotOpenException = NULL;
jclass		clsIncorrectVertexTypeException = NULL;
jclass		clsInvalidPositionException = NULL;
jclass		clsNoSuchNodeException = NULL;
jclass		clsNoSuchVertexException = NULL;

jmethodID	storageCreationExceptionCMID = NULL;
jmethodID	storageIsNotOpenExceptionCMID = NULL;
jmethodID	incorrectVertexTypeExceptionCMID = NULL;
jmethodID	invalidPositionExceptionCMID = NULL;
jmethodID	noSuchNodeExceptionCMID = NULL;
jmethodID	noSuchVertexExceptionCMID = NULL;

/*
 * Class objects for Storage, Node, Value and Vertex, and method IDs
 * for their constructors:
 */

jclass		clsStorage = NULL;
jclass		clsNode = NULL;
jclass		clsValue = NULL;
jclass		clsVertex = NULL;

jmethodID	storageCMID = NULL;
jmethodID	nodeCMID = NULL;
jmethodID	valueCMID = NULL;
jmethodID	vertexCMID = NULL;

/*
 * These field IDs are for access to the fields of Storage, Node and Vertex.
 */

jfieldID	sIndexID = NULL;
jfieldID	nIndexID = NULL;
jfieldID	nsIndexID = NULL;
jfieldID	vIndexID = NULL;
jfieldID	vsIndexID = NULL;

/*
 * These field IDs are for access to the various fields of Value.
 */

jfieldID	typeID = NULL;
jfieldID	integerValueID = NULL;
jfieldID	doubleValueID = NULL;
jfieldID	stringValueID = NULL;
jfieldID	bytesValueID = NULL;
jfieldID	nodeValueID = NULL;

/*
 * Lock access to the storage cache.
 */

static void
LockStorages(JNIEnv *envp)
{
    envp->MonitorEnter(storageLock);
}

/*
 * Unlock access to the storage cache.
 */

static void
ReleaseStorages(JNIEnv *envp)
{
    envp->MonitorExit(storageLock);
}

/*
 * Check that a given index represents a legal storage.
 */

bool
GetValidStorage(JNIEnv *envp, int i, int g, e4_Storage &ss)
{
    if (i < 0) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "negative index");
	return false;
    }
    LockStorages(envp);
    if (i >= storageSize) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "index out of range");
	ReleaseStorages(envp);
	return false;
    }
    if (!storages[i].s.IsValid()) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "invalid storage");
	ReleaseStorages(envp);
	return false;
    }
    if (storages[i].generation != g) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "incorrect generation counter");
	ReleaseStorages(envp);
	return false;
    }

    ss = storages[i].s;
    ReleaseStorages(envp);

    return true;
}

/*
 * Allocate cache space for a storage.
 */

static int
AllocStorageIndex(JNIEnv *envp, e4_Storage s)
{
    StorageCacheRecord *newStorages;
    int i, newStorageSize;

    /*
     * Locate a space in the current cache.
     */

    LockStorages(envp);
    for (i = 0; i < storageSize; i++) {
	if (!storages[i].s.IsValid()) {
	    storages[i].s = s;
	    storages[i].generation = generation++;
	    ReleaseStorages(envp);
	    return i;
	}
    }

    /*
     * Reallocate the cache to a bigger cache.
     */

    newStorageSize = storageSize + STATIC_STORAGE_SIZE;
    newStorages = new StorageCacheRecord [newStorageSize];
    for (i = 0; i < storageSize; i++) {
	newStorages[i] = storages[i];
	storages[i].s = invalidStorage;
    }
    for (i = storageSize; i < newStorageSize; i++) {
	newStorages[i].s = invalidStorage;
    }
    if (storages != staticStorages) {
	delete [] storages;
    }
    storages = newStorages;
    storageSize = newStorageSize;

    /*
     * Locate a space in the current cache.
     */

    for (i = 0; i < storageSize; i++) {
	if (!storages[i].s.IsValid()) {
	    storages[i].s = s;
	    storages[i].generation = generation++;
	    ReleaseStorages(envp);
	    return i;
	}
    }

    /*
     * Still cannot find space in the cache, abort.
     */

    ReleaseStorages(envp);
    envp->ThrowNew(clsStorageCreationException,
		   "cannot allocate cache for storage");
    return -1;
}

/*
 * Set the i'th storage with the given generation to the supplied storage.
 *
 * This is used most often to set a given storage to invalidStorage, to
 * release our reference to the underlying e4Graph storage.
 */

void
SetValidStorage(JNIEnv *envp, jint index, jint g, e4_Storage ns)
{
    if (index < 0) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "negative index");
	return;
    }
    LockStorages(envp);
    if (index >= storageSize) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "index out of range");
	ReleaseStorages(envp);
	return;
    }
    if (!storages[index].s.IsValid()) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "invalid storage");
	ReleaseStorages(envp);
	return;
    }
    if (storages[index].generation != g) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "incorrect generation counter");
	ReleaseStorages(envp);
	return;
    }

    storages[index].s = ns;
    ReleaseStorages(envp);
}

/*
 * Retrieve a node from its storage.
 */

bool
GetValidNode(JNIEnv *envp, int sindex, int gen, int nindex, e4_Node &nn)
{
    e4_NodeUniqueID nuid;
    e4_Storage s;

    if (!GetValidStorage(envp, sindex, gen, s)) {
	return false;
    }

    nuid.SetID(nindex);
    nuid.SetSP(s.GetTemporaryUID());

    if (s.GetNodeFromID(nuid, nn)) {
	return true;
    }
    envp->ThrowNew(clsNoSuchNodeException, "invalid node ID");
    return false;
}

/*
 * Retrieve a vertex from its storage.
 */

bool
GetValidVertex(JNIEnv *envp, int sindex, int gen, int vindex, e4_Vertex &vv)
{
    e4_VertexUniqueID vuid;
    e4_Storage s;

    if (!GetValidStorage(envp, sindex, gen, s)) {
	return false;
    }

    vuid.SetID(vindex);
    vuid.SetSP(s.GetTemporaryUID());

    if (s.GetVertexFromID(vuid, vv)) {
	return true;
    }
    envp->ThrowNew(clsNoSuchVertexException, "invalid vertex ID");
    return false;
}

/*
 * Initialize all the classes, jmethodIDs and jfieldIDs used by the
 * natives in this package.
 */

JNIEXPORT void JNICALL
Java_com_e4graph_Storage_initNativeIDs(JNIEnv *envp, jclass cls)
{
    static bool initialized = false;
    jclass clsObject = NULL;
    jmethodID objectCMID = NULL;
    int i;

    /*
     * Do only once.
     */

    if (initialized) {
	return;
    }
    initialized = true;

    /*
     * Initialize the array of storages.
     */

    for (i = 0; i < STATIC_STORAGE_SIZE; i++) {
	storages[i].s = invalidStorage;
    }
    generation = 0;

    /*
     * Initialize all classes, field IDs and method IDs.
     */

    clsStorage = envp->FindClass("com/e4graph/Storage");
    if (clsStorage == NULL) {
	goto error;
    }
    clsNode = envp->FindClass("com/e4graph/Node");
    if (clsNode == NULL) {
	goto error;
    }
    clsVertex = envp->FindClass("com/e4graph/Vertex");
    if (clsVertex == NULL) {
	goto error;
    }

    clsValue = envp->FindClass("com/e4graph/Value");
    if (clsValue == NULL) {
	goto error;
    }

    clsStorageCreationException = 
	envp->FindClass("com/e4graph/StorageCreationException");
    if (clsStorageCreationException == NULL) {
	goto error;
    }
    storageCreationExceptionCMID =
	envp->GetStaticMethodID(clsStorageCreationException, "<init>", "()V");
    if (storageCreationExceptionCMID == NULL) {
	goto error;
    }

    clsStorageIsNotOpenException =
	envp->FindClass("com/e4graph/StorageIsNotOpenException");
    if (clsStorageIsNotOpenException == NULL) {
	goto error;
    }
    storageIsNotOpenExceptionCMID =
	envp->GetStaticMethodID(clsStorageIsNotOpenException, "<init>", "()V");
    if (storageIsNotOpenExceptionCMID == NULL) {
	goto error;
    }

    clsIncorrectVertexTypeException =
	envp->FindClass("com/e4graph/IncorrectVertexTypeException");
    if (clsIncorrectVertexTypeException == NULL) {
	goto error;
    }
    incorrectVertexTypeExceptionCMID =
	envp->GetStaticMethodID(clsIncorrectVertexTypeException,
				"<init>",
				"()V");
    if (incorrectVertexTypeExceptionCMID == NULL) {
	goto error;
    }

    clsInvalidPositionException =
	envp->FindClass("com/e4graph/InvalidPositionException");
    if (clsInvalidPositionException == NULL) {
	goto error;
    }
    invalidPositionExceptionCMID =
	envp->GetStaticMethodID(clsInvalidPositionException, "<init>", "()V");
    if (invalidPositionExceptionCMID == NULL) {
	goto error;
    }

    clsNoSuchNodeException =
	envp->FindClass("com/e4graph/NoSuchNodeException");
    if (clsNoSuchNodeException == NULL) {
	goto error;
    }
    noSuchNodeExceptionCMID =
	envp->GetStaticMethodID(clsNoSuchNodeException, "<init>", "()V");
    if (noSuchNodeExceptionCMID == NULL) {
	goto error;
    }

    clsNoSuchVertexException =
	envp->FindClass("com/e4graph/NoSuchVertexException");
    if (clsNoSuchVertexException == NULL) {
	goto error;
    }
    noSuchVertexExceptionCMID =
	envp->GetStaticMethodID(clsNoSuchVertexException, "<init>", "()V");
    if (noSuchVertexExceptionCMID == NULL) {
	goto error;
    }

    sIndexID = envp->GetFieldID(clsStorage, "index", "I");
    if (sIndexID == NULL) {
	goto error;
    }
    nIndexID = envp->GetFieldID(clsNode, "nodeIndex", "I");
    if (nIndexID == NULL) {
	goto error;
    }
    nsIndexID = envp->GetFieldID(clsNode, "storageIndex", "I");
    if (nsIndexID == NULL) {
	goto error;
    }
    vIndexID = envp->GetFieldID(clsVertex, "vertexIndex", "I");
    if (vIndexID == NULL) {
	goto error;
    }
    vsIndexID = envp->GetFieldID(clsVertex, "storageIndex", "I");
    if (vsIndexID == NULL) {
	goto error;
    }

    typeID = envp->GetFieldID(clsValue, "vertexType", "I");
    if (typeID == NULL) {
	goto error;
    }
    integerValueID = envp->GetFieldID(clsValue, "integerValue", "I");
    if (integerValueID == NULL) {
	goto error;
    }
    doubleValueID = envp->GetFieldID(clsValue, "doubleValue", "D");
    if (doubleValueID == NULL) {
	goto error;
    }
    stringValueID = envp->GetFieldID(clsValue, "stringValue",
				     "Ljava/lang/String;");
    if (stringValueID == NULL) {
	goto error;
    }
    bytesValueID = envp->GetFieldID(clsValue, "bytesValue", "[B");
    if (bytesValueID == NULL) {
	goto error;
    }
    nodeValueID = envp->GetFieldID(clsValue, "nodeValue",
				   "Lcom/e4graph/Node;");
    if (nodeValueID == NULL) {
	goto error;
    }

    nodeCMID = envp->GetStaticMethodID(clsNode, "<init>", "(III)V");
    if (nodeCMID == NULL) {
	goto error;
    }

    vertexCMID = envp->GetStaticMethodID(clsVertex, "<init>", "(III)V");
    if (vertexCMID == NULL) {
      goto error;
    }

    storageCMID = envp->GetStaticMethodID(clsStorage, "<init>", "(II)V");
    if (storageCMID == NULL) {
	goto error;
    }

    valueCMID = envp->GetStaticMethodID(clsValue, "<init>", "()V");
    if (valueCMID == NULL) {
	goto error;
    }

    /*
     * Create the object used to synchronize access to the storage cache.
     */

    clsObject = envp->FindClass("java/lang/Object");
    if (clsObject == NULL) {
	goto error;
    }
    objectCMID = envp->GetStaticMethodID(clsObject, "<init>", "()V");
    if (objectCMID == NULL) {
	goto error;
    }
    storageLock = envp->NewObject(clsObject, objectCMID);
    if (storageLock == NULL) {
	goto error;
    }

    /*
     * All done, return.
     */

    return;

error:
    /*
     * If there's already an exception lodged in the env, leave it there.
     */

    if (envp->ExceptionOccurred()) {
	return;
    }

    /*
     * No exception is present, so lodge a fatal error.
     */

    envp->FatalError("j4graph: fatal exception during initialization");
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Storage_createStorage(JNIEnv *envp, jclass cls,
				       jstring name, jstring driver,
				       jint state)
{
    jboolean ncopy, dcopy;
    const char *nchars = envp->GetStringUTFChars(name, &ncopy);
    const char *dchars = envp->GetStringUTFChars(driver, &dcopy);
    e4_Storage s(nchars, dchars, state);

    if (ncopy) {
	envp->ReleaseStringUTFChars(name, nchars);
    }
    if (dcopy) {
	envp->ReleaseStringUTFChars(driver, dchars);
    }

    if (!s.IsValid()) {
	envp->ThrowNew(clsStorageCreationException,
		       "invalid storage");
	return -1;
    }

    return AllocStorageIndex(envp, s);
}

/*
 * Given a storage index that represents a valid storage, retrieve the
 * generation for that storage.
 */

JNIEXPORT jint JNICALL
Java_com_e4graph_Storage_getStorageGeneration(JNIEnv *envp, jclass cls, jint i)
{
    if (i < 0) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "negative index");
	return -1;
    }
    LockStorages(envp);
    if (i >= storageSize) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "index out of range");
	ReleaseStorages(envp);
	return -1;
    }
    if (!storages[i].s.IsValid()) {
	envp->ThrowNew(clsStorageIsNotOpenException,
		       "invalid storage");
	ReleaseStorages(envp);
	return -1;
    }
    return storages[i].generation;
}

/*
 * Given a storage index and generation, retrieve the string name of the
 * storage.
 */

JNIEXPORT jstring JNICALL
Java_com_e4graph_Storage_getNameOfStorage(JNIEnv *envp, jclass cls,
					  jint i, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, i, g, s)) {
	return NULL;
    }
    return envp->NewStringUTF(s.GetName());
}

/*
 * Given a storage index and generation, retrieve the string name of the
 * driver of this storage.
 */

JNIEXPORT jstring JNICALL
Java_com_e4graph_Storage_getDriverOfStorage(JNIEnv *envp, jclass cls,
					    jint i, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, i, g, s)) {
	return NULL;
    }
    return envp->NewStringUTF(s.GetDriver());
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Storage_getStorage(JNIEnv *envp, jclass cls, jint i, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, i, g, s)) {
	return NULL;
    }
    return envp->NewObject(clsStorage, storageCMID, i, g);
}

JNIEXPORT jstring JNICALL
Java_com_e4graph_Storage_version(JNIEnv *envp, jclass cls)
{
    return envp->NewStringUTF(e4_Storage::version());
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Storage_isValid1(JNIEnv *envp, jobject me, jint i, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, i, g, s)) {
	envp->ExceptionClear();
	return JNI_FALSE;
    }
    return JNI_TRUE;
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Storage_setState1(JNIEnv *envp, jobject me,
				   jint index, jint g, jint newstate)
{
    e4_Storage s;

    if (!GetValidStorage(envp, index, g, s)) {
	return -1;
    }
    return s.SetState(newstate);
}

JNIEXPORT jint JNICALL
Java_com_e4graph_Storage_getState1(JNIEnv *envp, jobject me,
				   jint index, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, index, g, s)) {
	return -1;
    }
    return s.GetState();
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Storage_commit1(JNIEnv *envp, jobject me, jint index, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, index, g, s)) {
	return JNI_FALSE;
    }
    if (s.Commit()) {
	return JNI_TRUE;
    }
    return JNI_FALSE;
}

JNIEXPORT void JNICALL
Java_com_e4graph_Storage_close1(JNIEnv *envp, jobject me, jint index, jint g)
{
    SetValidStorage(envp, index, g, invalidStorage);
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Storage_copyTo1(JNIEnv *envp, jobject me,
				 jint myindex, jint myg,
				 jint hisindex, jint hisg,
				 jboolean forceCommit)
{
    e4_Storage mys, hiss;

    if (!GetValidStorage(envp, myindex, myg, mys) || 
	!GetValidStorage(envp, hisindex, hisg, hiss)) {
	return JNI_FALSE;
    }
    if (mys.CopyTo(hiss, (forceCommit == JNI_TRUE) ? true : false)) {
	return JNI_TRUE;
    }
    return JNI_FALSE;
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Storage_Delete1(JNIEnv *envp, jobject me,
				 jint index, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, index, g, s)) {
	return JNI_FALSE;
    }
    if (s.Delete()) {
	SetValidStorage(envp, index, g, invalidStorage);
	return JNI_TRUE;
    }
    return JNI_TRUE;
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Storage_getRoot1(JNIEnv *envp, jobject me, jint index, jint g)
{
    e4_Storage s;
    e4_Node n;
    e4_NodeUniqueID nuid;

    if (!GetValidStorage(envp, index, g, s)) {
	return NULL;
    }
    if (!s.GetRootNode(n)) {
	envp->ThrowNew(clsNoSuchNodeException, "cannot retrieve root node");
	return NULL;
    }
    (void) n.GetUniqueID(nuid);
    return envp->NewObject(clsNode, nodeCMID, index, g, nuid.GetUniqueID());
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Storage_setRoot1(JNIEnv *envp, jobject me,
				  jint index, jint g,
				  jint nindex, jint nsindex)
{
    e4_Storage s;
    e4_Node n;
    e4_NodeUniqueID nuid;

    if (index != nsindex) {
	envp->ThrowNew(clsNoSuchNodeException,
		       "new root node not in same storage");
	return JNI_FALSE;
    }
    if (!GetValidStorage(envp, index, g, s)) {
	return JNI_FALSE;
    }

    nuid.SetID(nindex);
    nuid.SetSP(s.GetTemporaryUID());

    if (!s.GetNodeFromID(nuid, n)) {
	envp->ThrowNew(clsNoSuchNodeException, "invalid node ID");
	return JNI_FALSE;
    }

    if (!s.SetRootNode(n)) {
	envp->ThrowNew(clsNoSuchNodeException, "cannot set new root node");
	return JNI_FALSE;
    }
    return JNI_TRUE;
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Storage_isStable1(JNIEnv *envp, jobject me, 
				     jint index, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, index, g, s)) {
	return JNI_FALSE;
    }
    return (s.IsStable()) ? JNI_TRUE: JNI_FALSE;
}

JNIEXPORT void JNICALL
Java_com_e4graph_Storage_markUnstable1(JNIEnv *envp, jobject me,
				    jint index, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, index, g, s)) {
	return;
    }
    s.MarkUnstable();
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Storage_getNode1(JNIEnv *envp, jobject me,
				  jint index, jint g)
{
    e4_Storage s;
    e4_Node n;
    e4_NodeUniqueID nuid;

    if (!GetValidStorage(envp, index, g, s)) {
	return NULL;
    }
    if (!s.CreateDetachedNode(n)) {
	return NULL;
    }
    (void) n.GetUniqueID(nuid);
    return envp->NewObject(clsNode, nodeCMID, index, g, nuid.GetUniqueID());
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Storage_getNodeVertex1(JNIEnv *envp, jobject me,
					jint index, jint g,
					jstring name,
					jint nindex, jint nsindex)
{
    e4_Storage s;
    e4_Node n;
    e4_NodeUniqueID nuid;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    jboolean copy;
    const char *vn;

    if (index != nsindex) {
	envp->ThrowNew(clsNoSuchNodeException, "node not in same storage");
	return NULL;
    }
    if (!GetValidStorage(envp, index, g, s)) {
	return NULL;
    }
    nuid.SetID(nindex);
    nuid.SetSP(s.GetTemporaryUID());

    if (!s.GetNodeFromID(nuid, n)) {
	envp->ThrowNew(clsNoSuchNodeException, "invalid node");
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!s.CreateDetachedVertex(vn, n, v)) {
	if (copy) {
	    envp->ReleaseStringUTFChars(name, vn);
	}
	envp->ThrowNew(clsNoSuchVertexException, "could not create vertex");
	return NULL;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    (void) v.GetUniqueID(vuid);
    return envp->NewObject(clsVertex, vertexCMID, index,
			   g, vuid.GetUniqueID());
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Storage_getIntVertex1(JNIEnv *envp, jobject me, jint index,
				       jint g, jstring name, jint i)
{
    e4_Storage s;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    jboolean copy;
    const char *vn;

    if (!GetValidStorage(envp, index, g, s)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!s.CreateDetachedVertex(vn, (int) i, v)) {
	if (copy) {
	    envp->ReleaseStringUTFChars(name, vn);
	}
	envp->ThrowNew(clsNoSuchVertexException, "could not create vertex");
	return NULL;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    (void) v.GetUniqueID(vuid);
    return envp->NewObject(clsVertex, vertexCMID, index,
			   g, vuid.GetUniqueID());
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Storage_getDoubleVertex1(JNIEnv *envp, jobject me, jint index,
					  jint g, jstring name, jdouble d)
{
    e4_Storage s;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    jboolean copy;
    const char *vn;

    if (!GetValidStorage(envp, index, g, s)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &copy);
    if (!s.CreateDetachedVertex(vn, d, v)) {
	if (copy) {
	    envp->ReleaseStringUTFChars(name, vn);
	}
	envp->ThrowNew(clsNoSuchVertexException, "could not create vertex");
	return NULL;
    }
    if (copy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    (void) v.GetUniqueID(vuid);
    return envp->NewObject(clsVertex, vertexCMID, index,
			   g, vuid.GetUniqueID());
}

JNIEXPORT jobject JNICALL
Java_com_e4graph_Storage_getStringVertex1(JNIEnv *envp, jobject me, jint index,
					  jint g, jstring name, jstring val)
{
    e4_Storage s;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    jboolean ncopy, vcopy;
    const char *vn, *vv;

    if (!GetValidStorage(envp, index, g, s)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &ncopy);
    vv = envp->GetStringUTFChars(val, &vcopy);
    if (!s.CreateDetachedVertex(vn, vv, v)) {
	if (ncopy) {
	    envp->ReleaseStringUTFChars(name, vn);
	}
	if (vcopy) {
	    envp->ReleaseStringUTFChars(val, vv);
	}
	envp->ThrowNew(clsNoSuchVertexException, "could not create vertex");
	return NULL;
    }
    if (ncopy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (vcopy) {
	envp->ReleaseStringUTFChars(val, vv);
    }
    (void) v.GetUniqueID(vuid);
    return envp->NewObject(clsVertex, vertexCMID, index,
			   g, vuid.GetUniqueID());
}
JNIEXPORT jobject JNICALL
Java_com_e4graph_Storage_getBytesVertex1(JNIEnv *envp, jobject me, jint index,
					 jint g, jstring name,
					 jbyteArray jbytes)
{
    e4_Storage s;
    e4_Vertex v;
    e4_VertexUniqueID vuid;
    jboolean ncopy, bcopy;
    const char *vn;
    jbyte *thebytes;
    int len;

    if (!GetValidStorage(envp, index, g, s)) {
	return NULL;
    }
    vn = envp->GetStringUTFChars(name, &ncopy);
    thebytes = envp->GetByteArrayElements(jbytes, &bcopy);
    len = envp->GetArrayLength(jbytes);
    if (!s.CreateDetachedVertex(vn, thebytes, len, v)) {
	if (ncopy) {
	    envp->ReleaseStringUTFChars(name, vn);
	}
	if (bcopy) {
	    envp->ReleaseByteArrayElements(jbytes, thebytes, 0);
	}
	envp->ThrowNew(clsNoSuchVertexException, "could not create vertex");
	return NULL;
    }
    if (ncopy) {
	envp->ReleaseStringUTFChars(name, vn);
    }
    if (bcopy) {
	envp->ReleaseByteArrayElements(jbytes, thebytes, 0);
    }
    (void) v.GetUniqueID(vuid);
    return envp->NewObject(clsVertex, vertexCMID, index,
			   g, vuid.GetUniqueID());
}

JNIEXPORT void JNICALL
Java_com_e4graph_Storage_doGC1(JNIEnv *envp, jobject me, jint index, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, index, g, s)) {
	return;
    }
    s.DoGC();
}

JNIEXPORT jboolean JNICALL
Java_com_e4graph_Storage_needsGC1(JNIEnv *envp, jobject me, jint index, jint g)
{
    e4_Storage s;

    if (!GetValidStorage(envp, index, g, s)) {
	return JNI_FALSE;
    }
    return (s.NeedsGC()) ? JNI_TRUE : JNI_FALSE;
}

/*
 * This native is for the com.e4graph.StorageIterator class. It is in this
 * file so that it will be able to use the file-static storage cache data
 * structures.
 */

JNIEXPORT jobject JNICALL
Java_com_e4graph_StorageIterator_computeNext(JNIEnv *envp, jclass cls, jint i)
{
    jobject obj;

    LockStorages(envp);
    for (i++; i < storageSize; i++) {
	if (storages[i].s.IsValid()) {
	    obj = envp->NewObject(clsStorage, storageCMID, i,
				  storages[i].generation);
	    ReleaseStorages(envp);
	    return obj;
	}
    }
    ReleaseStorages(envp);
    return NULL;
}
