/* * e4storage.cpp -- * * Implementation of the e4_Storage class defined in e4graph.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" /* * This instance of e4_Storage can be used (by assigning it to fields or * variables of type e4_Storage) to remove remaining references to instances * that you want to discard. Especially useful for removing remaining * references in malloc'ed memory. */ e4_Storage const invalidStorage; /* * Static methods to return version of e4Graph implemented. */ const char * e4_Storage::version() { return E4GRAPH_VERSION; } int e4_Storage::major_version() { return E4GRAPH_MAJOR; } int e4_Storage::minor_version() { return E4GRAPH_MINOR; } e4_ReleaseStatus e4_Storage::release_status() { return E4GRAPH_RELEASESTATUS; } int e4_Storage::release_iteration() { return E4GRAPH_RELEASEITER; } /* * Compute the version string for the version of e4Graph that wrote * a specific storage. */ const char * e4_Storage::storage_version(const char *fname, const char *drivername) { static char verbuf[128]; static char statstrings[] = {'a','b','.','p'}; int majver, minver, reliter; e4_ReleaseStatus relstat; if (!storage_version_info(fname, drivername, majver, minver, relstat, reliter)) { return NULL; } sprintf(verbuf, "%d.%d%c%d", majver, minver, statstrings[(int) relstat], reliter); return (const char *) verbuf; } /* * Return numeric constants representing the version of e4Graph that * wrote a specific storage. */ bool e4_Storage::storage_version_info(const char *fname, const char *drivername, int &maj, int &min, e4_ReleaseStatus &rs, int &ri) { return e4_StorageImpl::GetStorageVersionInfo(fname, drivername, maj, min, rs, ri); } /* * Default constructor: */ e4_Storage::e4_Storage() : e4_RefCount() {} /* * Constructor which assigns a value to the the implementation pointer. */ e4_Storage::e4_Storage(e4_StorageImpl *ip) : e4_RefCount(ip) {} /* * Copying constructor: */ e4_Storage::e4_Storage(const e4_Storage &referrer) : e4_RefCount(referrer) {} /* * Copying constructor that's given an e4_RefCount: */ e4_Storage::e4_Storage(const e4_RefCount &referrer) : e4_RefCount(referrer) { if ((impl != NULL) && (impl->Kind() != E4_RKSTORAGE)) { (void) e4_RefCount::operator=(invalidStorage); } } /* * Assignment operator: */ e4_Storage & e4_Storage::operator=(const e4_Storage &referrer) { return (e4_Storage &) e4_RefCount::operator=(referrer); } /* * Constructor that chooses a concrete representation for the underlying * implementation. */ e4_Storage::e4_Storage(const char *nm, const char *drivername) : e4_RefCount() { impl = e4_StorageImpl::GetStorage(nm, drivername, -1, E4_SPDEFAULTMASK); if (impl != NULL) { impl->IncrRefCount(); } } /* * Constructor as above that also allows the user to set the storage state. */ e4_Storage::e4_Storage(const char *nm, const char *drivername, int state) : e4_RefCount() { impl = e4_StorageImpl::GetStorage(nm, drivername, state, E4_SPDEFAULTMASK); if (impl != NULL) { impl->IncrRefCount(); } } /* * Constructor as above that also sets the storage permissions. */ e4_Storage::e4_Storage(const char *nm, const char *drivername, int state, int sp) : e4_RefCount() { impl = e4_StorageImpl::GetStorage(nm, drivername, state, sp); if (impl != NULL) { impl->IncrRefCount(); } } /* * Set and get the states for this storage. */ int e4_Storage::SetState(int statemask) const { if (impl == NULL) { return -1; } return ((e4_StorageImpl *) impl)->SetState(statemask); } int e4_Storage::GetState() const { if (impl == NULL) { return -1; } return ((e4_StorageImpl *) impl)->GetState(); } /* * Get the storage permissions. Setting is only supported during construction. */ int e4_Storage::GetPermissions() const { if (impl == NULL) { return 0; } return ((e4_StorageImpl *) impl)->GetPermissions(); } /* * Commit the storage now. */ bool e4_Storage::Commit() const { if (impl != NULL) { return ((e4_StorageImpl *) impl)->Commit(); } return false; } /* * Copy this storage to another storage. */ bool e4_Storage::CopyTo(e4_Storage otherStorage, bool forceCommit) const { if (!IsValid() || !otherStorage.IsValid()) { return false; } if (!((e4_StorageImpl *) impl)-> CopyTo((e4_StorageImpl *) otherStorage.impl)) { return false; } if (forceCommit) { return otherStorage.Commit(); } return true; } /* * Destroy this storage. If auto-commit is set, first commit. */ bool e4_Storage::Delete() { if (impl != NULL) { if ((((e4_StorageImpl *) impl)->GetState() & E4_COMMITATCLOSE) == E4_COMMITATCLOSE) { ((e4_StorageImpl *) impl)->Commit(); } ((e4_StorageImpl *) impl)->Destroy(); impl->DecrRefCount(); } impl = NULL; return true; } /* * Get and set the root node. */ bool e4_Storage::GetRootNode(e4_Node &n) const { e4_NodeImpl *nnip; if (impl == NULL) { return false; } nnip = ((e4_StorageImpl *) impl)->GetRootNode(); if ((nnip == NULL) || (!nnip->IsValid())) { return false; } e4_Node nn(nnip); n = nn; return true; } bool e4_Storage::SetRootNode(e4_Node n) const { e4_Storage hisstorage; if (!n.IsValid() || (impl == NULL) || (!n.GetStorage(hisstorage)) || (*this != hisstorage)) { return false; } ((e4_StorageImpl *) impl)->SetRootNode((e4_NodeImpl *) n.impl); return true; } /* * Given a unique ID, obtain the referenced node. */ bool e4_Storage::GetNodeFromID(e4_NodeUniqueID nid, e4_Node &n) const { e4_NodeImpl *nnip; if (impl == NULL) { return false; } nnip = ((e4_StorageImpl *) impl)->FindNode(nid.GetUniqueID()); if (nnip == NULL) { return false; } e4_Node nn(nnip); n = nn; return true; } /* * Given a unique ID, obtain the vertex referenced by the ID. */ bool e4_Storage::GetVertexFromID(e4_VertexUniqueID vid, e4_Vertex &v) const { e4_VertexImpl *vvip; if (impl == NULL) { return false; } vvip = ((e4_StorageImpl *) impl)->GetVertex(vid.GetUniqueID()); if (vvip == NULL) { return false; } e4_Vertex vv(vvip); v = vv; return true; } /* * Given a unique ID for a vertex, get the next vertex after it in the same * node. The e4_DetachChoice argument determines which kind of vertex we * are looking for: detached, attached or both. */ bool e4_Storage::FindNextVertex(int vertexID, e4_VisitMethod vm, int vf, int nameID, int nodeID, int parentID, e4_VertexType typeID, e4_DetachChoice dc, e4_Vertex &f) const { e4_VertexImpl *fi; if (impl == NULL) { return false; } fi = ((e4_StorageImpl *) impl)->FindNextVertex(vertexID, vm, vf, nameID, nodeID, parentID, typeID, dc); if (fi == NULL) { return false; } e4_Vertex ff(fi); f = ff; return true; } /* * Given a unique ID for a node, find the next one after it in * the storage. The e4_DetachChoice argument determines which kind of * node we are looking for: detached (including the root), attached (+ root), * or both. */ bool e4_Storage::FindNextNode(int nodeID, e4_DetachChoice dc, e4_Node &n) const { e4_NodeImpl *ni; if (impl == NULL) { return false; } while (1) { ni = ((e4_StorageImpl *) impl)->FindNextNode(nodeID); if (ni == NULL) { return false; } /* * Whether or not the node is detached, create the wrapper. * This ensures that if the node is not one we want, its * reference count and referenced status is correctly maintained. */ e4_Node nn(ni); /* * Is this node what we are searching for? */ switch (dc) { case E4_DCDETACHED: if (ni->IsDetached()) { n = nn; return true; } break; case E4_DCATTACHED: if (!ni->IsDetached() || ni->IsRoot()) { n = nn; return true; } break; case E4_DCBOTH: n = nn; return true; } /* * The current node is not one we want to return, so set up * the next iteration by updating the starting nodeID. */ nodeID = ni->GetUniqueID(); } /* * Some compilers insist on this, so.. */ return false; } /* * Is this storage stable? */ bool e4_Storage::IsStable() const { if (impl == NULL) { return false; } return ((e4_StorageImpl *) impl)->IsStable(); } void e4_Storage::MarkUnstable() const { if (impl != NULL) { ((e4_StorageImpl *) impl)->MarkUnstable(); } } /* * Get the name of this storage. */ const char * e4_Storage::GetName() const { if (impl == NULL) { return NULL; } return ((e4_StorageImpl *) impl)->GetName(); } /* * Get the ID of the storage driver used by this storage. */ const char * e4_Storage::GetDriver() const { if (impl == NULL) { return NULL; } return ((e4_StorageImpl *) impl)->GetDriver(); } bool e4_Storage::CreateDetachedNode(e4_Node &n) const { e4_NodeImpl *nip; if (impl == NULL) { return false; } nip = ((e4_StorageImpl *) impl)->CreateDetachedNode(); if (nip == NULL) { return false; } e4_Node nn(nip); n = nn; if (((e4_StorageImpl *) impl)->HasCallbacks(E4_ECADDNODE)) { ((e4_StorageImpl *) impl)->CauseEventInternal(E4_ECADDNODE, nip, NULL); } return true; } bool e4_Storage::CreateDetachedVertex(const char *nm, e4_Node n, e4_Vertex &v) const { e4_VertexImpl *vip; if (impl == NULL) { return false; } vip = ((e4_StorageImpl *) impl)->CreateDetachedVertex(nm, (e4_NodeImpl *) n.impl); if (vip == NULL) { return false; } e4_Vertex vv(vip); v = vv; if (((e4_StorageImpl *) impl)->HasCallbacks(E4_ECADDVERTEX)) { ((e4_StorageImpl *) impl)->CauseEventInternal(E4_ECADDVERTEX, vip, NULL); } return true; } bool e4_Storage::CreateDetachedVertex(const char *nm, int i, e4_Vertex &v) const { e4_VertexImpl *vip; if (impl == NULL) { return false; } vip = ((e4_StorageImpl *) impl)->CreateDetachedVertex(nm, i); if (vip == NULL) { return false; } e4_Vertex vv(vip); v = vv; if (((e4_StorageImpl *) impl)->HasCallbacks(E4_ECADDVERTEX)) { ((e4_StorageImpl *) impl)->CauseEventInternal(E4_ECADDVERTEX, vip, NULL); } return true; } bool e4_Storage::CreateDetachedVertex(const char *nm, double d, e4_Vertex &v) const { e4_VertexImpl *vip; if (impl == NULL) { return false; } vip = ((e4_StorageImpl *) impl)->CreateDetachedVertex(nm, d); if (vip == NULL) { return false; } e4_Vertex vv(vip); v = vv; if (((e4_StorageImpl *) impl)->HasCallbacks(E4_ECADDVERTEX)) { ((e4_StorageImpl *) impl)->CauseEventInternal(E4_ECADDVERTEX, vip, NULL); } return true; } bool e4_Storage::CreateDetachedVertex(const char *nm, const char *s, e4_Vertex &v) const { e4_VertexImpl *vip; if (impl == NULL) { return false; } vip = ((e4_StorageImpl *) impl)->CreateDetachedVertex(nm, s); if (vip == NULL) { return false; } e4_Vertex vv(vip); v = vv; if (((e4_StorageImpl *) impl)->HasCallbacks(E4_ECADDVERTEX)) { ((e4_StorageImpl *) impl)->CauseEventInternal(E4_ECADDVERTEX, vip, NULL); } return true; } bool e4_Storage::CreateDetachedVertex(const char *nm, const void *b, int nb, e4_Vertex &v) const { e4_VertexImpl *vip; if (impl == NULL) { return false; } vip = ((e4_StorageImpl *) impl)->CreateDetachedVertex(nm, b, nb); if (vip == NULL) { return false; } e4_Vertex vv(vip); v = vv; if (((e4_StorageImpl *) impl)->HasCallbacks(E4_ECADDVERTEX)) { ((e4_StorageImpl *) impl)->CauseEventInternal(E4_ECADDVERTEX, vip, NULL); } return true; } bool e4_Storage::CreateDetachedVertex(const char *nm, const e4_Value &vv, e4_Vertex &v) const { if (impl == NULL) { return false; } switch (vv.vertexType) { case E4_VTNODE: return CreateDetachedVertex(nm, vv.n, v); case E4_VTINT: return CreateDetachedVertex(nm, vv.u.i, v); case E4_VTDOUBLE: return CreateDetachedVertex(nm, vv.u.d, v); case E4_VTSTRING: return CreateDetachedVertex(nm, vv.u.s, v); case E4_VTBINARY: return CreateDetachedVertex(nm, vv.u.b.bytes, vv.u.b.nbytes, v); default: return false; } } /* * Get a value for a statistic being collected for this storage. */ bool e4_Storage::GetStatistic(e4_Space sp, e4_SpaceStat st, int &v) const { if (impl == NULL) { return false; } return ((e4_StorageImpl *) impl)->GetStatistic(sp, st, v); } /* * Intern a name. */ int e4_Storage::InternName(const char *nm) const { if (impl == NULL) { return E4_VERTEXNOTFOUND; } return ((e4_StorageImpl *) impl)->InternName(nm, true); } /* * Get a node given its node ID: */ e4_NodeImpl * e4_Storage::GetNode(int nodeID) const { if (!IsValid()) { return NULL; } return ((e4_StorageImpl *) impl)->FindOrCreateNode(nodeID); } /* * Get the name of a vertex given the ID of the name. */ const char * e4_Storage::GetName(int nameID) const { if (!IsValid()) { return NULL; } return ((e4_StorageImpl *) impl)->NameFromNameID(nameID); } /* * Return the storage implementation for this storage: */ e4_StorageImpl * e4_Storage::GetStorageImpl() const { if (!IsValid()) { return NULL; } return (e4_StorageImpl *) impl; } /* * Do a GC. */ void e4_Storage::DoGC() const { if (IsValid()) { ((e4_StorageImpl *) impl)->DoGC(); } } /* * Is a GC needed? */ bool e4_Storage::NeedsGC() const { if (!IsValid()) { return false; } return ((e4_StorageImpl *) impl)->NeedsGC(); } /* * Return the kind identifier for this instance. */ e4_RefKind e4_Storage::Kind() const { return E4_RKSTORAGE; } /* * Callback facility: Declare or remove callbacks for specific kinds of * events. */ bool e4_Storage::DeclareCallback(int eventCode, e4_CallbackFunction fn, void *cd) { if (!IsValid()) { return false; } if (((e4_StorageImpl *) impl)->AddCallback(eventCode, (void *) fn, cd)) { return true; } return false; } bool e4_Storage::DeleteCallback(int eventCode, e4_CallbackFunction fn, void *cd) { if (!IsValid()) { return false; } if (((e4_StorageImpl *) impl)->DelCallback(eventCode, (void *) fn, cd)) { return true; } return false; } bool e4_Storage::CauseEvent(int eventCode, const e4_RefCount &r, void *csdata) { if (!IsValid() || !r.IsValid() || (eventCode < E4_FIRSTUSERDEFINEDEVENTCODE) || (eventCode > E4_LASTUSERDEFINEDEVENTCODE)) { return false; } if (!((e4_StorageImpl *) impl)->HasCallbacks(eventCode)) { return true; } if (!((e4_StorageImpl *) impl)->CauseEvent(eventCode, r, csdata)) { return false; } return true; } /* * Timestamp facility: */ int e4_Storage::GetTimeStamp() const { if (!IsValid()) { return -1; } return ((e4_StorageImpl *) impl)->GetTimeStamp(); } int e4_Storage::GetTimeStampFor(int em) const { if (!IsValid()) { return -1; } return ((e4_StorageImpl *) impl)->GetTimeStampFor(em); } bool e4_Storage::HasOccurredSince(int timestamp, int em) const { if (!IsValid()) { return false; } if ((timestamp < 0) || (timestamp > GetTimeStamp())) { return false; } return ((e4_StorageImpl *) impl)->HasOccurredSince(timestamp, em); } /* * Facility for managing user defined event codes: */ static int userDefinedEventCodes = 0; bool e4_Storage::DefineEventCode(int &eventcode) { int i; for (i = E4_FIRSTUSERDEFINEDEVENTCODE; i <= E4_LASTUSERDEFINEDEVENTCODE; i++) { if ((userDefinedEventCodes & (1 << i)) == 0) { userDefinedEventCodes |= (1 << i); eventcode = (1< E4_LASTUSERDEFINEDEVENTCODE)) { return false; } if ((userDefinedEventCodes & eventcode) == 0) { return false; } userDefinedEventCodes &= (~(eventcode)); return true; } bool e4_Storage::IsEventCodeDefined(int eventcode) { if ((eventcode < E4_FIRSTUSERDEFINEDEVENTCODE) || (eventcode > E4_LASTUSERDEFINEDEVENTCODE)) { return false; } if ((userDefinedEventCodes & eventcode) == 0) { return false; } return true; }