/*
 * i4node.cpp --
 *
 *	This file provides the implementation of the e4_NodeImpl class defined
 *	in e4graphimpl.h.
 *
 *	Authors: Jacob Levy and Jean-Claude Wippler.
 *		 jyl@best.com	jcw@equi4.com
 *
 * 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 "e4graphimpl.h"

/*
 * These constants define cache key values for caching by rank:
 */

#define E4_CACHEIDBYRANK	-1
#define E4_CACHERANKBYID	-2

/*
 * This constructor is for internal use (by friend classes) only:
 */

e4_NodeImpl::e4_NodeImpl(e4_StorageImpl *s, int ni)
    : e4_RefCounter(),
      storage(s),
      nodeID(ni),
      flags(0),
      cachePolicy(E4_CACHEINCREMENTAL),
      cachedVertexIDs(NULL),
      cacheNonEmpty(false)
{}

/*
 * The destructor:
 */

e4_NodeImpl::~e4_NodeImpl()
{
    e4_StorageImpl *lsp = storage;

    if (storage != NULL) {

	/*
	 * Don't bother about cleanups if the storage itself is invalid.
	 */

	if (!storage->IsValid()) {
	    return;
	}

	/*
	 * Remove this node from the cache.
	 */

        storage->ForgetNode(nodeID);

	/*
	 * Can't use the "storage" instance variable
	 * because ForgetNode resets it to NULL.
	 *
	 * If this node is detached, and not the root, it became unreachable.
	 * If GC is not deferred clean up right away.
	 */

	if (lsp->DRV_IsDetachedNodeID(nodeID) &&
	    (lsp->DRV_GetRootNodeID() != nodeID)) {
	    lsp->RegisterUnreachableNodeID(nodeID);
	    if ((lsp->GetState() & E4_AUTOGC) == E4_AUTOGC) {
		lsp->DoGC();
	    } else {
		lsp->SetNeedsGC(true);
	    }
	}
    }

    /*
     * Clean up the cache of name+nth and rank mappings to vertex IDs.
     */

    if (cachedVertexIDs != NULL) {
	e4_DeleteHashTable(cachedVertexIDs);
	free((char *) cachedVertexIDs);
    }
}

/*
 * This method is called when the reference count goes to (or below) zero.
 */

void
e4_NodeImpl::NotReferenced()
{
    delete this;
}

/*
 * Precache all information about this node's vertices.
 */

void
e4_NodeImpl::PreCache()
{
    e4_HashTable *names;
    e4_HashEntry *ep;
    int nameID, vertexID, isnew, rank, nth;
    union {
	void *v;
	int i;
    } u;

    if (!storage->CachingVertexIDs()) {
	return;
    }
    names = e4_NewHashTable(E4_ONE_WORD_KEY);
    for (vertexID = GetFirstVertexID(), rank = 1;
	 vertexID != E4_VERTEXNOTFOUND;
	 vertexID = storage->DRV_NextVertexID(vertexID), rank++) {
	nameID = storage->DRV_NameIDFromVertexID(vertexID);
	u.i = nameID;
	ep = E4_CREATEHASHENTRY(names, (char *) u.v, &isnew);
	if (isnew) {
	    E4_SETHASHVALUE(ep, 1);
	    nth = 1;
	} else {
	    u.v = (void *) E4_GETHASHVALUE(ep);
	    nth = 1 + u.i;
	    E4_SETHASHVALUE(ep, u.v);
	}
	CacheVertexIDByName(nameID, nth, vertexID);
	CacheVertexIDByRank(rank, vertexID);
	CacheVertexRankByID(vertexID, rank);
    }
    e4_DeleteHashTable(names);
    free((char *) names);
}

/*
 * Precache all information requested by the provided mask about this
 * node's vertices.
 */

void
e4_NodeImpl::PreCache(int mask)
{
    e4_HashTable *names;
    e4_HashEntry *ep;
    int nameID, vertexID, isnew, rank, nth;
    union {
	void *v;
	int i;
    } u;

    if ((mask == E4_CACHEINCREMENTAL) || (!storage->CachingVertexIDs())) {
	return;
    }
    names = e4_NewHashTable(E4_ONE_WORD_KEY);
    for (vertexID = GetFirstVertexID(), rank = 1;
	 vertexID != E4_VERTEXNOTFOUND;
	 vertexID = storage->DRV_NextVertexID(vertexID), rank++) {
	nameID = storage->DRV_NameIDFromVertexID(vertexID);
	u.i = nameID;
	ep = E4_CREATEHASHENTRY(names, (char *) u.v, &isnew);
	if (isnew) {
	    E4_SETHASHVALUE(ep, 1);
	    nth = 1;
	} else {
	    u.v = (void *) E4_GETHASHVALUE(ep);
	    nth = 1 + u.i;
	    E4_SETHASHVALUE(ep, u.v);
	}
	if ((mask & E4_AUTOCACHENAMES) == E4_AUTOCACHENAMES) {
	    CacheVertexIDByName(nameID, nth, vertexID);
	}
	if ((mask & E4_AUTOCACHERANKS) == E4_AUTOCACHERANKS) {
	    CacheVertexIDByRank(rank, vertexID);
	    CacheVertexRankByID(vertexID, rank);
	}
    }
    e4_DeleteHashTable(names);
    free((char *) names);
}

/*
 * This method is called to invalidate the cache mapping name+nth and
 * rank to vertex IDs.
 */

void
e4_NodeImpl::FlushCache()
{
    if (cacheNonEmpty) {
	if (cachedVertexIDs != NULL) {
	    e4_DeleteHashTable(cachedVertexIDs);
	    free((char *) cachedVertexIDs);
	}
	cachedVertexIDs = e4_NewHashTable(E4_TWO_WORDS_KEY);
    }
    cacheNonEmpty = false;
    if (cachePolicy != E4_CACHEINCREMENTAL) {
	PreCache();
    }
}

/*
 * Get a cached vertex ID by name+nth. Returns the ID or E4_VERTEXNOTFOUND.
 */

int
e4_NodeImpl::GetCachedVertexIDByName(int nameID, int nth) const
{
    int key[2];
    e4_HashEntry *ep;
    union {
	void *v;
	int i;
    } u;

    if ((nth < 1) || (nameID < 0) || (cachedVertexIDs == NULL)) {
	return E4_VERTEXNOTFOUND;
    }
    key[0] = nth;
    key[1] = nameID;
    ep = E4_FINDHASHENTRY(cachedVertexIDs, (const char *) key);
    if (ep != NULL) {
	u.v = E4_GETHASHVALUE(ep);
	return u.i;
    }
    return E4_VERTEXNOTFOUND;
}

/*
 * Get a cached vertex ID by rank. Returns the ID or E4_VERTEXNOTFOUND.
 */

int
e4_NodeImpl::GetCachedVertexIDByRank(int rank) const
{
    int key[2];
    e4_HashEntry *ep;
    union {
	void *v;
	int i;
    } u;

    if ((rank < 1) || (cachedVertexIDs == NULL)) {
	return E4_VERTEXNOTFOUND;
    }
    key[0] = rank;
    key[1] = E4_CACHEIDBYRANK;
    ep = E4_FINDHASHENTRY(cachedVertexIDs, (const char *) key);
    if (ep != NULL) {
	u.v = E4_GETHASHVALUE(ep);
	return u.i;
    }
    return E4_VERTEXNOTFOUND;
}

/*
 * Get a cached vertex rank by vertex ID. returns the rank
 * E4_VERTEXNOTFOUND.
 */

int
e4_NodeImpl::GetCachedVertexRankByID(int vid) const
{
    int key[2];
    int rank = E4_VERTEXNOTFOUND;
    e4_HashEntry *ep;
    union {
	void *v;
	int i;
    } u;

    if ((vid < 0) || (cachedVertexIDs == NULL)) {
	return E4_VERTEXNOTFOUND;
    }
    key[0] = vid;
    key[1] = E4_CACHERANKBYID;
    ep = E4_FINDHASHENTRY(cachedVertexIDs, (const char *) key);
    if (ep != NULL) {
	u.v = E4_GETHASHVALUE(ep);
	rank = u.i;
    }
    return rank;
}

/*
 * Cache a vertex ID by name + nth.
 */

void
e4_NodeImpl::CacheVertexIDByName(int nameID, int nth, int vid)
{
    int key[2];
    e4_HashEntry *ep;
    int isnew;
    union {
	void *v;
	int i;
    } u;

    /*
     * Only cache if the storage allows.
     */

    if (!storage->CachingVertexIDs()) {
	return;
    }

    /*
     * Only cache reasonable arguments.
     */

    if ((nameID < 0) || (nth < 1) || (vid < 0)) {
	return;
    }
    if (cachedVertexIDs == NULL) {
	cachedVertexIDs = e4_NewHashTable(E4_TWO_WORDS_KEY);
    }
    key[0] = nth;
    key[1] = nameID;
    ep = E4_CREATEHASHENTRY(cachedVertexIDs, (char *) key, &isnew);
    if ((ep != NULL) && (isnew != 0)) {
	u.i = vid;
	E4_SETHASHVALUE(ep, u.v);
    }
    cacheNonEmpty = true;
}

/*
 * Cache a vertex ID by rank.
 */

void
e4_NodeImpl::CacheVertexIDByRank(int rank, int vid)
{
    int key[2];
    e4_HashEntry *ep;
    int isnew;
    union {
	void *v;
	int i;
    } u;

    /*
     * Only cache if the storage allows.
     */

    if (!storage->CachingVertexIDs()) {
	return;
    }

    /*
     * Only cache reasonable arguments.
     */

    if ((rank < 1) || (vid < 0)) {
	return;
    }
    if (cachedVertexIDs == NULL) {
	cachedVertexIDs = e4_NewHashTable(E4_TWO_WORDS_KEY);
    }
    key[0] = rank;
    key[1] = E4_CACHEIDBYRANK;
    ep = E4_CREATEHASHENTRY(cachedVertexIDs, (char *) key, &isnew);
    if ((ep != NULL) && (isnew != 0)) {
	u.i = vid;
	E4_SETHASHVALUE(ep, u.v);
    }
    cacheNonEmpty = true;
}

/*
 * Cache a vertex rank by vertex ID.
 */

void
e4_NodeImpl::CacheVertexRankByID(int vid, int rank)
{
    int key[2];
    e4_HashEntry *ep;
    int isnew;
    union {
	void *v;
	int i;
    } u;

    /*
     * Only cache if the storage allows.
     */

    if (!storage->CachingVertexIDs()) {
	return;
    }

    /*
     * Only cache reasonable arguments.
     */

    if ((rank < 1) || (vid < 0)) {
	return;
    }
    if (cachedVertexIDs == NULL) {
	cachedVertexIDs = e4_NewHashTable(E4_TWO_WORDS_KEY);
    }
    key[0] = vid;
    key[1] = E4_CACHERANKBYID;
    ep = E4_CREATEHASHENTRY(cachedVertexIDs, (char *) key, &isnew);
    if ((ep != NULL) && (isnew != 0)) {
	u.i = rank;
	E4_SETHASHVALUE(ep, u.v);
    }
    cacheNonEmpty = true;
}

/*
 * Set an existing vertex or add a vertex with a node value.
 */
        
e4_NodeImpl *
e4_NodeImpl::SetNthNode(const char *nm, int nth)
{
    int i, rank, nameID, hisNodeID;
    e4_NodeImpl *hisnode = NULL;
    e4_VertexImpl *v;

    if (storage == NULL) {
        return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return NULL;
    }
    nameID = storage->InternName(nm, true);
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return NULL;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    hisNodeID = storage->DRV_ReserveNodeID();
    storage->MarkUnstable();
    if (storage->DRV_SetVertexByIndexToNode(i, hisNodeID)) {
	hisnode = storage->FindOrCreateNode(hisNodeID);
	hisnode->IncrRefCount();
	storage->RecordTimeStamp(E4_ECADDNODE | E4_ECMODVERTEX);
	if (storage->HasCallbacks(E4_ECADDNODE)) {
	    storage->CauseEventInternal(E4_ECADDNODE, hisnode, NULL);
	}
	if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	    v = storage->FindReferencedVertex(i);
	    if (v != NULL) {
		storage->CauseEventInternal(E4_ECMODVERTEX,
					    v,
					    (void *) E4_ERMVMODVALUE);
	    }
	}
    }
    return hisnode;
}

e4_NodeImpl *
e4_NodeImpl::SetNodeByRank(int rank)
{
    int i, hisNodeID;
    e4_NodeImpl *hisnode = NULL;
    e4_VertexImpl *v;
    
    if (storage == NULL) {
        return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return NULL;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return NULL;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    hisNodeID = storage->DRV_ReserveNodeID();
    storage->MarkUnstable();
    if (storage->DRV_SetVertexByIndexToNode(i, hisNodeID)) {
	hisnode = storage->FindOrCreateNode(hisNodeID);
	hisnode->IncrRefCount();
	storage->RecordTimeStamp(E4_ECADDNODE | E4_ECMODVERTEX);
	if (storage->HasCallbacks(E4_ECADDNODE)) {
	    storage->CauseEventInternal(E4_ECADDNODE, hisnode, NULL);
	}
	if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	    v = storage->FindReferencedVertex(i);
	    if (v != NULL) {
		storage->CauseEventInternal(E4_ECMODVERTEX,
					    v,
					    (void *) E4_ERMVMODVALUE);
	    }
	}
    }
    return hisnode;
}

e4_NodeImpl *
e4_NodeImpl::AddNode(const char *nm, e4_InsertOrder io, int &rank)
{
    int i, hisNodeID;
    e4_NodeImpl *hisnode = NULL;

    if (storage == NULL) {
	return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return NULL;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return NULL;
    }
    storage->MarkUnstable();
    hisNodeID = storage->DRV_ReserveNodeID();
    if (storage->DRV_SetVertex(i, 
			       storage->InternName(nm, true), 
			       E4_VTNODE,
			       hisNodeID)) {
	hisnode = storage->FindOrCreateNode(hisNodeID);
	hisnode->IncrRefCount();
	storage->RecordTimeStamp(E4_ECADDNODE | E4_ECADDVERTEX | E4_ECMODNODE);
	if (storage->HasCallbacks(E4_ECADDNODE)) {
	    storage->CauseEventInternal(E4_ECADDNODE, hisnode, NULL);
	}
	if (storage->HasCallbacks(E4_ECMODNODE)) {
	    storage->CauseEventInternal(E4_ECMODNODE,
					this,
					(void *) E4_ERMNMOVVERTEX);
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    return hisnode;
}

bool
e4_NodeImpl::SetNthVertex(const char *nm, int nth, int value)
{
    int i, rank, nameID;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = storage->InternName(nm, true);
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndex(i, value)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetNthVertex(const char *nm, int nth, double value)
{
    int i, rank, nameID;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = storage->InternName(nm, true);
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndex(i, value)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetNthVertex(const char *nm, int nth, const char *value)
{
    int i, rank, nameID;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = storage->InternName(nm, true);
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndex(i, value)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetNthVertex(const char *nm,
			  int nth, 
			  const void *bytes,
			  int nbytes)
{
    int i, rank, nameID;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = storage->InternName(nm, true);
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndex(i, bytes, nbytes)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX, 
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetNthVertexToNode(const char *nm, int nth, int childID)
{
    int i, rank, nameID;
    e4_VertexImpl *v;
    e4_NodeImpl *nip;
    bool wasdetached;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = storage->InternName(nm, true);
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    wasdetached = storage->DRV_IsDetachedNodeID(childID);
    if (!storage->DRV_SetVertexByIndexToNode(i, childID)) {
	return false;
    }
    storage->MarkUnstable();
    if (wasdetached) {
	storage->RecordTimeStamp(E4_ECMODVERTEX | E4_ECATTNODE);
    } else {
	storage->RecordTimeStamp(E4_ECMODVERTEX);
    }
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    if (wasdetached && (storage->HasCallbacks(E4_ECATTNODE))) {
	nip = storage->FindReferencedNode(childID);
	if (nip != NULL) {
	    storage->CauseEventInternal(E4_ECATTNODE, nip, NULL);
	    nip->ClearFlags(E4_CBDETACHDELIVERED);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetVertexByRank(int rank, int value)
{
    int i;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndex(i, value)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetVertexByRank(int rank, double value)
{
    int i;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndex(i, value)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetVertexByRank(int rank, const char *value)
{
    int i;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndex(i, value)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetVertexByRank(int rank, const void *bytes, int nbytes)
{
    int i;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndex(i, bytes, nbytes)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

bool
e4_NodeImpl::SetVertexByRankToNode(int rank, int childID)
{
    int i;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (!storage->DRV_SetVertexByIndexToNode(i, childID)) {
	return false;
    }
    storage->MarkUnstable();
    storage->RecordTimeStamp(E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVMODVALUE);
	}
    }
    return true;
}

/*
 * Add a vertex to this node at a given position.
 */

bool
e4_NodeImpl::AddVertex(const char *nm,
		       e4_InsertOrder io, 
		       int &rank,
		       int value)
{
    int i, j;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return false;
    }
    storage->MarkUnstable();
    j = storage->DRV_AddInt(value);
    storage->DRV_SetVertex(i, storage->InternName(nm, true), E4_VTINT, j);
    if (io != E4_IOLAST) {
	FlushCache();
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX);
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNADDVERTEX);
    }
    return true;
}

bool
e4_NodeImpl::AddVertex(const char *nm,
		       e4_InsertOrder io, 
		       int &rank,
		       double value)
{
    int i, j;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return false;
    }
    storage->MarkUnstable();
    j = storage->DRV_AddDouble(value);
    storage->DRV_SetVertex(i, storage->InternName(nm, true), E4_VTDOUBLE, j);
    if (io != E4_IOLAST) {
	FlushCache();
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX);
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this, 
				    (void *) E4_ERMNADDVERTEX);
    }
    return true;
}

bool
e4_NodeImpl::AddVertex(const char *nm,
		       e4_InsertOrder io,
		       int &rank,
		       const char *value)
{
    int i, j;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return false;
    }
    storage->MarkUnstable();
    j = storage->DRV_AddString(value);
    storage->DRV_SetVertex(i, storage->InternName(nm, true), E4_VTSTRING, j);
    if (io != E4_IOLAST) {
	FlushCache();
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX);
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNADDVERTEX);
    }
    return true;
}

bool
e4_NodeImpl::AddVertex(const char *nm,
		       e4_InsertOrder io,
		       int &rank,
		       const void *bytes,
		       int nbytes)
{
    int i, j;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return false;
    }
    storage->MarkUnstable();
    j = storage->DRV_AddBinary(bytes, nbytes);
    storage->DRV_SetVertex(i, storage->InternName(nm, true), E4_VTBINARY, j);
    if (io != E4_IOLAST) {
	FlushCache();
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    storage->RecordTimeStamp(E4_ECMODNODE);
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNADDVERTEX);
    }
    return true;
}

bool
e4_NodeImpl::AddVertexWithNode(const char *nm,
			       e4_InsertOrder io,
			       int &rank,
			       int childID)
{
    int i;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return false;
    }
    storage->MarkUnstable();
    if (storage->DRV_SetVertex(i, 
			       storage->InternName(nm, true),
			       E4_VTNODE,
			       childID)) {
	if (io != E4_IOLAST) {
	    FlushCache();
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
	storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX | E4_ECADDNODE);
	if (storage->HasCallbacks(E4_ECMODNODE)) {
	    storage->CauseEventInternal(E4_ECMODNODE,
					this,
					(void *) E4_ERMNADDVERTEX);
	}
	return true;
    }
    return false;
}

/*
 * Add a vertex and return an e4_VertexImpl for it in one operation.
 */

e4_VertexImpl *
e4_NodeImpl::AddVertexRef(const char *nm,
			  e4_InsertOrder io, 
			  int &rank,
			  int value)
{
    int i, j;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return NULL;
    }
    storage->MarkUnstable();
    j = storage->DRV_AddInt(value);
    storage->DRV_SetVertex(i, storage->InternName(nm, true), E4_VTINT, j);
    if (io != E4_IOLAST) {
	FlushCache();
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    v = storage->GetVertex(i);
    v->IncrRefCount();
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX);
    if (storage->HasCallbacks(E4_ECADDVERTEX)) {
	storage->CauseEventInternal(E4_ECADDVERTEX, v, NULL);
    }
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this, 
				    (void *) E4_ERMNADDVERTEX);
    }
    return v;
}

e4_VertexImpl *
e4_NodeImpl::AddVertexRef(const char *nm,
			  e4_InsertOrder io,
			  int &rank,
			  double value)
{
    int i, j;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return NULL;
    }
    storage->MarkUnstable();
    j = storage->DRV_AddDouble(value);
    storage->DRV_SetVertex(i, storage->InternName(nm, true), E4_VTDOUBLE, j);
    if (io != E4_IOLAST) {
	FlushCache();
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    v = storage->GetVertex(i);
    v->IncrRefCount();
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX);
    if (storage->HasCallbacks(E4_ECADDVERTEX)) {
	storage->CauseEventInternal(E4_ECADDVERTEX, v, NULL);
    }
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNADDVERTEX);
    }
    return v;
}

e4_VertexImpl *
e4_NodeImpl::AddVertexRef(const char *nm,
			  e4_InsertOrder io,
			  int &rank, 
			  const char *value)
{
    int i, j;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return NULL;
    }
    storage->MarkUnstable();
    j = storage->DRV_AddString(value);
    storage->DRV_SetVertex(i, storage->InternName(nm, true), E4_VTSTRING, j);
    if (io != E4_IOLAST) {
	FlushCache();
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    v = storage->GetVertex(i);
    v->IncrRefCount();
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX);
    if (storage->HasCallbacks(E4_ECADDVERTEX)) {
	storage->CauseEventInternal(E4_ECADDVERTEX, v, NULL);
    }
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNADDVERTEX);
    }
    return v;
}

e4_VertexImpl *
e4_NodeImpl::AddVertexRef(const char *nm,
			  e4_InsertOrder io,
			  int &rank, 
			  const void *bytes,
			  int nbytes)
{
    int i, j;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return NULL;
    }
    storage->MarkUnstable();
    j = storage->DRV_AddBinary(bytes, nbytes);
    storage->DRV_SetVertex(i, storage->InternName(nm, true), E4_VTBINARY, j);
    if (io != E4_IOLAST) {
	FlushCache();
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    v = storage->GetVertex(i);
    v->IncrRefCount();
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX);
    if (storage->HasCallbacks(E4_ECADDVERTEX)) {
	storage->CauseEventInternal(E4_ECADDVERTEX, v, NULL);
    }
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNADDVERTEX);
    }
    return v;
}

e4_VertexImpl *
e4_NodeImpl::AddVertexRefWithNode(const char *nm, 
				  e4_InsertOrder io,
				  int &rank,
				  int childID)
{
    int i;
    e4_VertexImpl *v = NULL;

    if (storage == NULL) {
	return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return NULL;
    }
    storage->MarkUnstable();
    if (storage->DRV_SetVertex(i, 
			       storage->InternName(nm, true),
			       E4_VTNODE,
			       childID)) {
	if (io != E4_IOLAST) {
	    FlushCache();
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
	v = storage->GetVertex(i);
	v->IncrRefCount();
	storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX);
	if (storage->HasCallbacks(E4_ECADDVERTEX)) {
	    storage->CauseEventInternal(E4_ECADDVERTEX, v, NULL);
	}
	if (storage->HasCallbacks(E4_ECMODNODE)) {
	    storage->CauseEventInternal(E4_ECMODNODE,
					this,
					(void *) E4_ERMNADDVERTEX);
	}
    }
    return v;
}

e4_VertexImpl *
e4_NodeImpl::AddNodeRef(const char *nm,
			e4_InsertOrder io,
			int &rank,
			e4_NodeImpl *&node)
{
    int i, hisNodeID;
    e4_VertexImpl *v = NULL;

    if (storage == NULL) {
	return NULL;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return NULL;
    }
    i = storage->DRV_AddVertex(nodeID, io, rank);
    if (i == E4_VERTEXNOTCREATED) {
	return NULL;
    }
    storage->MarkUnstable();
    hisNodeID = storage->DRV_ReserveNodeID();
    if (storage->DRV_SetVertex(i,
			       storage->InternName(nm, true),
			       E4_VTNODE,
			       hisNodeID)) {
	node = storage->FindOrCreateNode(hisNodeID);
	if (node == NULL) {
	    return NULL;
	}
	node->IncrRefCount();
	if (io != E4_IOLAST) {
	    FlushCache();
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
	v = storage->GetVertex(i);
	if (v == NULL) {
	    return NULL;
	}
	v->IncrRefCount();
	storage->RecordTimeStamp(E4_ECMODNODE | E4_ECADDVERTEX | E4_ECADDNODE);
	if (storage->HasCallbacks(E4_ECADDNODE)) {
	    storage->CauseEventInternal(E4_ECADDNODE, node, NULL);
	}
	if (storage->HasCallbacks(E4_ECADDVERTEX)) {
	    storage->CauseEventInternal(E4_ECADDVERTEX, v, NULL);
	}
	if (storage->HasCallbacks(E4_ECMODNODE)) {
	    storage->CauseEventInternal(E4_ECMODNODE,
					this,
					(void *) E4_ERMNADDVERTEX);
	}
    }
    return v;
}

/*
 * Get a value from a vertex:
 */

bool
e4_NodeImpl::GetNthVertex(const char *nm, int nth, e4_ValueImpl *&v)
{
    int i, rank, nameID;

    if (storage == NULL) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, v) == false)) {
	return false;
    }
    return true;
}

bool
e4_NodeImpl::GetNthVertex(const char *nm, int nth, e4_NodeImpl *&n)
{
    int i, rank, nameID;

    if (storage == NULL) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, n) == false)) {
	return false;
    }
    return true;
}

bool
e4_NodeImpl::GetNthVertex(const char *nm, int nth, int &value)
{
    int i, rank, nameID;

    if (storage == NULL) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, value) == false)) {
        return false;
    }
    return true;
}

bool
e4_NodeImpl::GetNthVertex(const char *nm, int nth, double &value)
{
    int i, rank, nameID;

    if (storage == NULL) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, value) == false)) {
	return false;
    }
    return true;
}

bool
e4_NodeImpl::GetNthVertex(const char *nm, int nth, const char *&value)
{
    int i, rank, nameID;

    if (storage == NULL) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, value) == false)) {
	return false;
    }
    return true;
}

bool
e4_NodeImpl::GetNthVertex(const char *nm, 
			  int nth,
			  const void *&bytes,
			  int &nbytes)
{
    int i, rank, nameID;

    if (storage == NULL) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) || 
	(storage->DRV_GetVertexByIndex(i, bytes, nbytes) == false)) {
	return false;
    }
    return true;
}

bool
e4_NodeImpl::GetVertexByRank(int rank, e4_ValueImpl *&value)
{
    int i;

    if (storage == NULL) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
	(storage->DRV_GetVertexByIndex(i, value) == false)) {
        return false;
    }
    return true;
}

bool
e4_NodeImpl::GetVertexByRank(int rank, e4_NodeImpl *&value)
{
    int i;

    if (storage == NULL) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, value) == false)) {
        return false;
    }
    return true;
}

bool
e4_NodeImpl::GetVertexByRank(int rank, int &value)
{
    int i;

    if (storage == NULL) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, value) == false)) {
	return false;
    }
    return true;
}

bool
e4_NodeImpl::GetVertexByRank(int rank, double &value)
{
    int i;

    if (storage == NULL) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, value) == false)) {
	return false;
    }
    return true;
}

bool
e4_NodeImpl::GetVertexByRank(int rank, const char *&value)
{
    int i;

    if (storage == NULL) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
        (storage->DRV_GetVertexByIndex(i, value) == false)) {
	return false;
    }
    return true;
}

bool
e4_NodeImpl::GetVertexByRank(int rank, const void *&bytes, int &nbytes)
{
    int i;

    if (storage == NULL) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if ((i == E4_VERTEXNOTFOUND) ||
	(storage->DRV_GetVertexByIndex(i, bytes, nbytes) == false)) {
	return false;
    }
    return true;
}

e4_VertexType
e4_NodeImpl::VertexType(const char *nm, int nth)
{
    int i, rank, nameID;

    if (storage == NULL) {
        return E4_VTUNKNOWN;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return E4_VTUNKNOWN;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return E4_VTUNKNOWN;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    return storage->DRV_VertexTypeFromVertexID(i);
}

e4_VertexType
e4_NodeImpl::VertexTypeByRank(int rank)
{
    int i;

    if (storage == NULL) {
        return E4_VTUNKNOWN;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return E4_VTUNKNOWN;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    return storage->DRV_VertexTypeFromVertexID(i);
}

const char *
e4_NodeImpl::VertexName(int rank)
{
    int i;

    if (storage == NULL) {
	return NULL;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return NULL;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    return storage->DRV_VertexNameFromVertexID(i);
}

/*
 * Rename a vertex identified by rank.
 */

bool
e4_NodeImpl::RenameVertex(int rank, const char *newname)
{
    int i;
    int nameID;
    e4_VertexImpl *v;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = storage->InternName(newname, true);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
    }
    if (!storage->DRV_RenameVertexByVertexID(i, nameID)) {
	return false;
    }
    storage->MarkUnstable();
    FlushCache();
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECMODVERTEX);
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNRENVERTEX);
    }
    if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	v = storage->FindReferencedVertex(i);
	if (v != NULL) {
	    storage->CauseEventInternal(E4_ECMODVERTEX,
					v,
					(void *) E4_ERMVRENAME);
	}
    }
    CacheVertexIDByRank(rank, i);
    CacheVertexRankByID(i, rank);
    return true;
}

/*
 * Compute the rank of the nth vertex with a given name.
 */

int
e4_NodeImpl::VertexRank(const char *nm, int nth)
{
    int i, nameID;
    int rank = E4_VERTEXNOTFOUND;

    if (storage == NULL) {
	return E4_VERTEXNOTFOUND;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return E4_VERTEXNOTFOUND;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return E4_VERTEXNOTFOUND;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    if (rank == E4_VERTEXNOTFOUND) {
	rank = GetCachedVertexRankByID(i);
	if (rank == E4_VERTEXNOTFOUND) {
	    i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	    if (i == E4_VERTEXNOTFOUND) {
		return E4_VERTEXNOTFOUND;
	    }
	    CacheVertexIDByName(nameID, nth, i);
	    CacheVertexIDByRank(rank, i);
	    CacheVertexRankByID(i, rank);
	}
    }

    return rank;
}

/*
 * Get the name of the ith vertex containing this node in its nth parent node.
 */

const char *
e4_NodeImpl::GetNameInParent(int nth, int ith) const
{
    int parentID;
    int vertexID;

    if (storage == NULL) {
        return NULL;
    }
    parentID = storage->DRV_GetParentNodeID(nodeID, nth);
    if (parentID == E4_NODENOTFOUND) {
        return NULL;
    }
    vertexID = storage->DRV_GetVertexIDInParent(parentID, nodeID, ith);
    if (vertexID == E4_VERTEXNOTFOUND) {
        return NULL;
    }
    return storage->DRV_VertexNameFromVertexID(vertexID);
}

/*
 * Get the name of the ith vertex containing this node in the parent node
 * p.
 */

const char *
e4_NodeImpl::GetNameInParent(e4_NodeImpl *p, int ith) const
{
    int vertexID;

    if ((storage == NULL) ||
	(p->storage != storage) ||
	(!storage->DRV_IsParentID(p->nodeID, nodeID))) {
        return NULL;
    }
    vertexID = storage->DRV_GetVertexIDInParent(p->nodeID, nodeID, ith);
    if (vertexID == E4_VERTEXNOTFOUND) {
        return NULL;
    }
    return storage->DRV_VertexNameFromVertexID(vertexID);
}

/*
 * Get the rank of the ith vertex containing this node in its nth parent node.
 */

int
e4_NodeImpl::GetRankInParent(int nth, int ith) const
{
    int parentID, rank;
        
    if (storage == NULL) {
        return E4_NODENOTFOUND;
    }
    parentID = storage->DRV_GetParentNodeID(nodeID, nth);
    if (parentID == E4_NODENOTFOUND) {
        return E4_NODENOTFOUND;
    }
    rank = storage->DRV_GetRankOfChildNode(parentID, nodeID, ith);
    if (rank == E4_VERTEXNOTFOUND) {
	return E4_NODENOTFOUND;
    }
    return rank;
}

/*
 * Get the rank of the ith vertex in the parent node p of this node whose
 * value is this node.
 */

int
e4_NodeImpl::GetRankInParent(e4_NodeImpl *p, int ith) const
{
   int rank;

   if ((storage == NULL) || (storage != p->storage) ||
       (!storage->DRV_IsParentID(p->nodeID, nodeID))) {
	return E4_NODENOTFOUND;
   }
   rank = storage->DRV_GetRankOfChildNode(p->nodeID, nodeID, ith);
   if (rank == E4_VERTEXNOTFOUND) {
	return E4_NODENOTFOUND;
   }
   return rank;
}

/*
 * Detach the nth vertex with the given name in this node.
 */

bool
e4_NodeImpl::DetachVertex(const char *nm, int nth)
{
    int nameID, rank, i, childID, mask;
    e4_VertexImpl *vp;
    e4_NodeImpl *np;
    bool detachedNode = false;
    bool hasNodeValue = false;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
    }
    FlushCache();
    if (storage->DRV_VertexTypeFromVertexID(i) == E4_VTNODE) {
	(void) storage->DRV_GetRawValue(i, childID);
	hasNodeValue = true;
    }
    if (!storage->DRV_DetachVertexByID(i)) {
	return false;
    }
    storage->MarkUnstable();
    mask = E4_ECMODNODE | E4_ECDETVERTEX;
    if ((hasNodeValue) && (storage->DRV_IsDetachedNodeID(childID))) {
	mask |= E4_ECDETNODE;
	detachedNode = true;
    }
    storage->RecordTimeStamp(mask);
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNDETVERTEX);
    }
    if (storage->HasCallbacks(E4_ECDETVERTEX)) {
	vp = storage->FindReferencedVertex(i);
	if (vp != NULL) {
	    storage->CauseEventInternal(E4_ECDETVERTEX, vp, NULL);
	    vp->SetFlags(E4_CBDETACHDELIVERED);
	}
    }
    if (detachedNode && (storage->HasCallbacks(E4_ECDETNODE))) {
	np = storage->FindReferencedNode(childID);
	if ((np != NULL) && (!np->HasFlags(E4_CBDETACHDELIVERED))) {
	    storage->CauseEventInternal(E4_ECDETNODE, np, NULL);
	    np->SetFlags(E4_CBDETACHDELIVERED);
	}
    }
    return true;
}

/*
 * Detach the vertex identified by the given rank.
 */

bool
e4_NodeImpl::DetachVertexByRank(int rank)
{
    int i, childID, mask;
    e4_VertexImpl *vp;
    e4_NodeImpl *np;
    bool detachedNode = false;
    bool hasNodeValue = false;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return false;
	}
    }
    FlushCache();
    if (storage->DRV_VertexTypeFromVertexID(i) == E4_VTNODE) {
	(void) storage->DRV_GetRawValue(i, childID);
	hasNodeValue = true;
    }
    if (!storage->DRV_DetachVertexByID(i)) {
	return false;
    }
    storage->MarkUnstable();
    mask = E4_ECMODNODE | E4_ECDETVERTEX;
    if ((hasNodeValue) && (storage->DRV_IsDetachedNodeID(childID))) {
	mask |= E4_ECDETNODE;
	detachedNode = true;
    }
    storage->RecordTimeStamp(mask);
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNDETVERTEX);
    }
    if (storage->HasCallbacks(E4_ECDETVERTEX)) {
	vp = storage->FindReferencedVertex(i);
	if ((vp != NULL) && (!vp->HasFlags(E4_CBDETACHDELIVERED))) {
	    storage->CauseEventInternal(E4_ECDETVERTEX,	vp, NULL);
	    vp->SetFlags(E4_CBDETACHDELIVERED);
	}
    }
    if (detachedNode && (storage->HasCallbacks(E4_ECDETNODE))) {
	np = storage->FindReferencedNode(childID);
	if ((np != NULL) && (!np->HasFlags(E4_CBDETACHDELIVERED))) {
	    storage->CauseEventInternal(E4_ECDETNODE, np, NULL);
	    np->SetFlags(E4_CBDETACHDELIVERED);
	}
    }
    return true;
}

/*
 * Detach the first vertex whose value is the given child node.
 */

bool
e4_NodeImpl::DetachFirstVertexWithNode(e4_NodeImpl *childImpl)
{
    e4_VertexImpl *vp;
    int i;

    if ((storage == NULL) || (childImpl == NULL) ||
	(childImpl->storage != storage)) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    i = storage->DRV_GetVertexIDInParent(nodeID, childImpl->nodeID, 1);
    if (i == E4_VERTEXNOTFOUND) {
	return false;
    }
    if (!storage->DRV_DetachVertexByID(i)) {
	return false;
    }
    storage->MarkUnstable();
    FlushCache();
    storage->RecordTimeStamp(E4_ECMODNODE | E4_ECDETVERTEX);
    if (storage->HasCallbacks(E4_ECMODNODE)) {
	storage->CauseEventInternal(E4_ECMODNODE,
				    this,
				    (void *) E4_ERMNDETVERTEX);
    }
    if (storage->HasCallbacks(E4_ECDETVERTEX)) {
	vp = storage->FindReferencedVertex(i);
	if ((vp != NULL) && (!vp->HasFlags(E4_CBDETACHDELIVERED))) {
	    storage->CauseEventInternal(E4_ECDETVERTEX, vp, NULL);
	    vp->SetFlags(E4_CBDETACHDELIVERED);
	}
    }
    return true;
}

/*
 * Get an e4_VertexImpl for a vertex specified by rank.
 */

e4_VertexImpl *
e4_NodeImpl::GetVertexRefByRank(int rank)
{
    int i;

    if (storage == NULL) {
        return NULL;
    }
    i = GetCachedVertexIDByRank(rank);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromRank(nodeID, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return NULL;
	}
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    return storage->GetVertex(i);
}

/*
 * Get an e4_VertexImpl from a vertex specified by name and nth.
 */

e4_VertexImpl *
e4_NodeImpl::GetVertexRef(const char *nm, int nth)
{
    int i, rank, nameID;

    if (storage == NULL) {
        return NULL;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return NULL;
    }
    i = GetCachedVertexIDByName(nameID, nth);
    if (i == E4_VERTEXNOTFOUND) {
	i = storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (i == E4_VERTEXNOTFOUND) {
	    return NULL;
	}
	CacheVertexIDByName(nameID, nth, i);
	CacheVertexIDByRank(rank, i);
	CacheVertexRankByID(i, rank);
    }
    return storage->GetVertex(i);
}

/*
 * Move a vertex identified by a vertex ID into the rank given in this
 * node.
 */

bool
e4_NodeImpl::MoveVertex(int vertexID, e4_InsertOrder order, int rank)
{
    if ((storage == NULL) ||
	((storage->GetPermissions() & E4_SPMODIFY) == 0)) {
	return false;
    }
    return storage->MoveVertex(nodeID, vertexID, order, rank);
}

/*
 * How many vertices are currently in this node?
 */

int
e4_NodeImpl::VertexCount() const
{
    if (storage == NULL) {
	return 0;
    }
    return storage->DRV_VertexCountFromNodeID(nodeID);
}

/*
 * How many vertices in this node have the given name?
 */

int
e4_NodeImpl::VertexCountWithName(const char *nm) const
{
    int nameID;

    if (storage == NULL) {
	return 0;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return 0;
    }
    return storage->DRV_VertexCountWithNameIDFromNodeID(nodeID,
							E4_VERTEXNOTFOUND,
							nameID);
}

/*
 * How many vertices in this node have the given type?
 */

int
e4_NodeImpl::VertexCountWithType(e4_VertexType tp) const
{
    if (storage == NULL) {
	return 0;
    }
    return storage->DRV_VertexCountWithTypeFromNodeID(nodeID, 
						      E4_VERTEXNOTFOUND,
						      tp);
}

/*
 * How many vertices in this node have the given value?
 */

int
e4_NodeImpl::VertexCountWithValue(const e4_Value &v) const
{
    if (storage == NULL) {
	return 0;
    }
    return storage->DRV_VertexCountWithValueFromNodeID(nodeID,
						       E4_VERTEXNOTFOUND,
						       v);
}

/*
 * Set a vertex to a new node:
 */

e4_NodeImpl *
e4_NodeImpl::SetNode(const char *nm)
{
    return SetNthNode(nm, 1);
}

/*
 * Does a vertex with the given name exist?
 */

bool
e4_NodeImpl::Exists(const char *nm, int nth)
{
    return (VertexRank(nm, nth) != E4_VERTEXNOTFOUND) ? true : false;
}

/*
 * Get the nth parent node:
 */

e4_NodeImpl *
e4_NodeImpl::GetParent(int nth) const
{
    if (storage == NULL) {
	return NULL;
    }
    return storage->DRV_GetParentNode(nodeID, nth);
}

/*
 * Retrieve the nth vertex in the given parent node p whose value is this node.
 */

e4_VertexImpl *
e4_NodeImpl::GetVertexRefFromParent(e4_NodeImpl *pp, int nth) const
{
    if ((storage == NULL) || (pp == NULL) || (pp->storage != storage)) {
	return NULL;
    }
    return storage->DRV_GetVertexRefFromParent(pp->nodeID, nodeID, nth);
}

/*
 * Retrieve the nth vertex in the ith parent node whose value is this node.
 */

e4_VertexImpl *
e4_NodeImpl::GetVertexRefFromParent(int i, int nth) const
{
    if (storage == NULL) {
	return NULL;
    }
    return storage->DRV_GetVertexRefFromIthParent(i, nodeID, nth);
}

/*
 * How many parents does this node have?
 */

int
e4_NodeImpl::ParentCount() const
{
    if (storage == NULL) {
	return E4_NODENOTFOUND;
    }
    return storage->DRV_ParentCount(nodeID);
}

/*
 * In how many vertices is this node the value?
 */

int
e4_NodeImpl::OccurrenceCount() const
{
    if (storage == NULL) {
	return E4_NODENOTFOUND;
    }
    return storage->DRV_OccurrenceCount(nodeID);
}

/*
 * How many vertices in this parent have this node as the value?
 */

int
e4_NodeImpl::OccurrenceCount(int parentID) const
{
    if (storage == NULL) {
	return E4_NODENOTFOUND;
    }
    return storage->DRV_OccurrenceCount(nodeID, parentID);
}

/*
 * What is the rank of the given parent in the list of parents
 * of this node?
 */

int
e4_NodeImpl::ParentRank(int parentID) const
{
    if (storage == NULL) {
	return E4_NODENOTFOUND;
    }
    return storage->DRV_ParentRank(nodeID, parentID);
}

/*
 * Retrieve the storage containing this node:
 */

e4_StorageImpl *
e4_NodeImpl::GetStorage() const
{
    return storage;
}

/*
 * Is this node the root?
 */

bool
e4_NodeImpl::IsRoot() const
{
    if (storage == NULL) {
	return false;
    }
    return (storage->GetRootNodeID() == nodeID) ? true : false;
}

/*
 * Retrieve an ID which uniquely identifies this node within its
 * containing storage.
 */

int
e4_NodeImpl::GetUniqueID() const
{
    if (storage == NULL) {
	return E4_NODENOTFOUND;
    }
    return nodeID;
}

/*
 * Detach this node from all vertices for which it is the value.
 */

bool
e4_NodeImpl::Detach()
{
    e4_NodeImpl *p;
    e4_VertexImpl *v;
    int i, l, mask;
    int *pids;
    bool hasCB;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }

    /*
     * Flush the cache for all referenced parents, because this is
     * an incompatible change.
     */
    
    l = ParentCount();
    if (l == 0) {
	return true;
    }
    for (i = 0, pids = new int[l]; i < l; i++) {
	pids[i] = storage->DRV_GetParentNodeID(nodeID, i+1);
    }
    if (!storage->DRV_DetachNodeByID(nodeID)) {
	delete[] pids;
	return false;
    }
    storage->MarkUnstable();

    /*
     * Deliver callbacks:
     *
     * - Deliver a detach node callback for this node.
     * - Deliver detach vertex callbacks for all referenced vertices
     *   whose value is this node that were previously attached.
     * - Deliver modify node callbacks for all referenced nodes that
     *   previously used to contain vertices whose value is this node.
     */

    mask = E4_ECDETNODE | E4_ECDETVERTEX;
    if (l > 0) {
	mask |= E4_ECMODNODE;
    }
    storage->RecordTimeStamp(mask);
    if (storage->HasCallbacks(E4_ECDETNODE)) {
	storage->CauseEventInternal(E4_ECDETNODE, this, NULL);
	SetFlags(E4_CBDETACHDELIVERED);
    }
    if (storage->HasCallbacks(E4_ECDETVERTEX)) {
	for (i = storage->DRV_GetFirstDetachedVertexIDWithNodeID(nodeID);
	     i != E4_NEXTNONE;
	     i = storage->DRV_GetNextDetachedVertexIDAfter(i)) {
	    v = storage->FindReferencedVertex(i);
	    if ((v != NULL) && (!v->HasFlags(E4_CBDETACHDELIVERED))) {
		storage->CauseEventInternal(E4_ECDETVERTEX, v, NULL);
		v->SetFlags(E4_CBDETACHDELIVERED);
	    }
	}
    }
    hasCB = storage->HasCallbacks(E4_ECMODNODE);
    for (i = 0; i <  l; i++) {
	p = storage->FindReferencedNode(pids[i]);
	if (p != NULL) {
	    p->FlushCache();
	    if (hasCB) {
		storage->CauseEventInternal(E4_ECMODNODE,
					    p,
					    (void *) E4_ERMNDETVERTEX);
	    }
	}
    }
    delete[] pids;
    return true;
}

/*
 * Is this node detached?
 */

bool
e4_NodeImpl::IsDetached() const
{
    if (storage == NULL) {
	return true;
    }
    return storage->DRV_IsDetachedNodeID(nodeID);
}

/*
 * Is this node valid?
 */

bool
e4_NodeImpl::IsValid() const
{
    if (storage == NULL) {
	return false;
    }
    return (storage->IsValid() && storage->DRV_IsLegalNodeID(nodeID));
}

/*
 * Retrieve the ID of the first and last vertex in this node.
 */

int
e4_NodeImpl::GetFirstVertexID() const
{
    if (storage == NULL) {
	return E4_VERTEXNOTFOUND;
    }
    return storage->DRV_GetFirstVertexID(nodeID);
}

int
e4_NodeImpl::GetLastVertexID() const
{
    if (storage == NULL) {
	return E4_VERTEXNOTFOUND;
    }
    return storage->DRV_GetLastVertexID(nodeID);
}

/*
 * Retrieve the node ID for use by the containing e4_StorageImpl.
 */

int
e4_NodeImpl::GetNodeID() const
{
    return nodeID;
}

/*
 * Set the storage field of this node.
 */

void
e4_NodeImpl::SetStorage(e4_StorageImpl *newstorage)
{
    storage = newstorage;
}

/*
 * Get/Set the user data associated with this node.
 */

bool
e4_NodeImpl::GetUserData(int &userData) const
{
    if (storage == NULL) {
	return false;
    }
    return storage->DRV_GetNodeUserData(nodeID, userData);
}

bool
e4_NodeImpl::SetUserData(int userData)
{
    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    if (storage->DRV_SetNodeUserData(nodeID, userData)) {
	storage->MarkUnstable();
	storage->RecordTimeStamp(E4_ECMODNODE);
	if (storage->HasCallbacks(E4_ECMODNODE)) {
	   storage->CauseEventInternal(E4_ECMODNODE,
				       this,
				       (void *) E4_ERMNMODUSERDATA);
	}
	return true;
    }
    return false;
}

/*
 * Get/Set the user data of a contained vertex:
 */

bool
e4_NodeImpl::SetVertexUserData(const char *nm, int nth, int userData)
{
    int vertexID, nameID, rank;
    e4_VertexImpl *vp;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    vertexID = GetCachedVertexIDByName(nameID, nth);
    if (vertexID == E4_VERTEXNOTFOUND) {
	vertexID =
	    storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (vertexID == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByName(nameID, nth, vertexID);
	CacheVertexIDByRank(rank, vertexID);
	CacheVertexRankByID(vertexID, rank);
    }
    if (storage->DRV_SetVertexUserData(vertexID, userData)) {
	storage->MarkUnstable();
	storage->RecordTimeStamp(E4_ECMODVERTEX);
	if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	    vp = storage->FindReferencedVertex(vertexID);
	    if (vp != NULL) {
		storage->CauseEventInternal(E4_ECMODVERTEX,
					    vp,
					    (void *) E4_ERMVMODUSERDATA);
	    }
	}
	return true;
    }
    return false;
}

bool
e4_NodeImpl::GetVertexUserData(const char *nm, int nth, int &userData)
{
    int vertexID, nameID, rank;

    if (storage == NULL) {
	return false;
    }
    nameID = storage->InternName(nm, false);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    vertexID = GetCachedVertexIDByName(nameID, nth);
    if (vertexID == E4_VERTEXNOTFOUND) {
	vertexID =
	    storage->DRV_VertexIDFromNthVertex(nodeID, nameID, nth, rank);
	if (vertexID == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByName(nameID, nth, vertexID);
	CacheVertexIDByRank(rank, vertexID);
	CacheVertexRankByID(vertexID, rank);
    }
    return storage->DRV_GetVertexUserData(vertexID, userData);
}

bool
e4_NodeImpl::SetVertexUserDataByRank(int rank, int userData)
{
    int vertexID;
    e4_VertexImpl *vp;

    if (storage == NULL) {
	return false;
    }
    if ((storage->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    vertexID = GetCachedVertexIDByRank(rank);
    if (vertexID == E4_VERTEXNOTFOUND) {
	vertexID =
	    storage->DRV_VertexIDFromRank(nodeID, rank);
	if (vertexID == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByRank(rank, vertexID);
	CacheVertexRankByID(vertexID, rank);
    }
    if (storage->DRV_SetVertexUserData(vertexID, userData)) {
	storage->MarkUnstable();
	storage->RecordTimeStamp(E4_ECMODVERTEX);
	if (storage->HasCallbacks(E4_ECMODVERTEX)) {
	    vp = storage->FindReferencedVertex(vertexID);
	    if (vp != NULL) {
		storage->CauseEventInternal(E4_ECMODVERTEX,
					    vp,
					    (void *) E4_ERMVMODUSERDATA);
	    }
	}
	return true;
    }
    return false;
}

bool
e4_NodeImpl::GetVertexUserDataByRank(int rank, int &userData)
{
    int vertexID;

    if (storage == NULL) {
	return false;
    }
    vertexID = GetCachedVertexIDByRank(rank);
    if (vertexID == E4_VERTEXNOTFOUND) {
	vertexID =
	    storage->DRV_VertexIDFromRank(nodeID, rank);
	if (vertexID == E4_VERTEXNOTFOUND) {
	    return false;
	}
	CacheVertexIDByRank(rank, vertexID);
	CacheVertexRankByID(vertexID, rank);
    }
    return storage->DRV_GetVertexUserData(vertexID, userData);
}
