/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #include #include #include "edf.h" /* prototypes */ static Object namedobjfunc(struct finfo *f, char **namelist, int flag); static Object numberedobjfunc(struct finfo *f, int *numlist, int idflag, int flag); static void printobjlist(struct finfo *f); /* for debug */ static struct hashidtolist { PseudoKey id; struct objlist *ol; }; /* init list - create hash table. */ Error _dxfinitobjlist(struct finfo *f) { if (!f) return ERROR; f->ol = NULL; f->ht = DXCreateHash(sizeof(struct hashidtolist), NULL, NULL); if (!f->ht) return ERROR; return OK; } /* delete list */ Error _dxfdeleteobjlist(struct finfo *f) { struct objlist *this, *next; if (!f) return ERROR; this = f->ol; while(this) { next = this->next; if (this->name) DXFree((Pointer)this->name); if (this->odelete == OBJ_CANDELETE) DXDelete(this->o); if (this->nrefs > 0) DXFree((Pointer)this->refs); if (this->gp) { DXFree((Pointer)this->gp->fname); DXFree((Pointer)this->gp->gbuf); DXFree((Pointer)this->gp); } DXFree((Pointer)this); this = next; } if (f->ht) DXDestroyHash(f->ht); f->ht = NULL; f->ol = NULL; return OK; } /* add a new object to the list. * inputs are the object id, the pointer to the object, and optionally * the name of the object (if name != NULL). if the list is originally * empty, initialize the objlist pointer also. the new object is added * to the head of the list - a fact the _dxfnumberedobjlist currently * counts on to be able to tell which is the most recently defined object. */ Error _dxfaddobjlist(struct finfo *f, int id, Object o, char *name, int start) { struct objlist *new; struct hashidtolist he; /* error if bad file ptr */ if (!f) return ERROR; if (id < 0) { if (DXQueryDebug("M")) DXMessage("id = -1, name = %s, o = 0x%08x", name?name:"NULL", o); } new = (struct objlist *)DXAllocateZero(sizeof(struct objlist)); if (!new) return ERROR; if (name) { new->name = (char *)DXAllocateLocal(strlen(name)+1); if (!new->name) return ERROR; strcpy(new->name, name); /* if there isn't already a name attribute, give it one */ if (o) { if (!DXGetAttribute(o, NAMEATTR)) DXSetAttribute(o, NAMEATTR, (Object)DXNewString(name)); } } if (o) DXReference(o); new->id = id; new->o = o; new->next = NULL; new->odelete = OBJ_CANDELETE; /* add new object block to start of list unless start == 0 */ if (!start && f->ol) { new->next = f->ol->next; f->ol->next = new; } else { new->next = f->ol; f->ol = new; } /* add to hash table for quick lookup */ he.id = id; he.ol = new; if (!DXInsertHashElement(f->ht, (Pointer)&he)) return ERROR; DXDebug("O", "added object 0x%08x, number %d, name %s", o, id, name ? name : ""); return OK; } /* set the object associated with an id */ Error _dxfsetobjptr(struct finfo *f, int id, Object o) { struct hashidtolist *he; if (!f || !f->ol || !f->ht || id < 0) return ERROR; /* all objects should be in hash table. */ he = (struct hashidtolist *)DXQueryHashElement(f->ht, (Key)&id); if (!he) return ERROR; /* if id is there, return ok if there isn't an object to set. */ if (!o) return ERROR; he->ol->o = o; DXReference(o); if (he->ol->name) { /* if there isn't already a name attribute, give it one */ if (o) { if (!DXGetAttribute(o, NAMEATTR)) DXSetAttribute(o, NAMEATTR, (Object)DXNewString(he->ol->name)); } } return OK; } /* mark the object associated with an id used. */ Error _dxfsetobjused(struct finfo *f, int id) { struct hashidtolist *he; if (!f || !f->ol || !f->ht || id < 0) return ERROR; /* all objects should be in hash table. */ he = (struct hashidtolist *)DXQueryHashElement(f->ht, (Key)&id); if (!he) return ERROR; he->ol->oused = OBJ_USED; return OK; } /* query if object associated with an id is used. */ Error _dxfisobjused(struct finfo *f, int id) { struct hashidtolist *he; if (!f || !f->ol || !f->ht || id < 0) return ERROR; he = (struct hashidtolist *)DXQueryHashElement(f->ht, (Key)&id); if (!he) return ERROR; return he->ol->oused == OBJ_USED ? OK : ERROR; } /* retrieve an object by id. returns NULL if object id isn't in list. * returns a pointer to the object, if the object is named, return a * pointer to the name in last parm. */ Error _dxflookobjlist(struct finfo *f, int id, Object *o, char **name) { struct hashidtolist *he; if (!f || !f->ol || !f->ht || id < 0) return ERROR; /* is object id in hash table? */ he = (struct hashidtolist *)DXQueryHashElement(f->ht, (Key)&id); if (!he) return ERROR; if (o) *o = he->ol->o; if (name) *name = he->ol->name; return OK; } /* retrieve a pointer to the the getby struct from an object by id. */ struct getby **_dxfgetobjgb(struct finfo *f, int id) { struct hashidtolist *he; if (!f || !f->ol || !f->ht || id < 0) return NULL; /* is object id in hash table? */ he = (struct hashidtolist *)DXQueryHashElement(f->ht, (Key)&id); if (!he) return NULL; return &(he->ol->gp); } /* used to keep track of which objects an object references. * add an object to the ref list if it isn't already there. */ Error _dxfobjusesobj(struct finfo *f, int id, int refd_id) { struct hashidtolist *he, *refd_he; struct objlist *this; if (!f || !f->ol || !f->ht || id < 0 || refd_id < 0) return ERROR; /* all objects should already be in hash table. */ he = (struct hashidtolist *)DXQueryHashElement(f->ht, (Key)&id); if (!he) return ERROR; refd_he = (struct hashidtolist *)DXQueryHashElement(f->ht, (Key)&refd_id); if (!refd_he) return ERROR; /* add an object to the list of objects referenced by this object * (should this code check for dups first???) * this code needs to NOT realloc each time... */ this = he->ol; if (this->nrefs == 0) { this->refs = (struct objlist **)DXAllocateLocal(sizeof(struct objlist *)); this->refs[0] = refd_he->ol; this->nrefs++; } else { this->refs = (struct objlist **)DXReAllocate(this->refs, sizeof(struct objlist *) * (this->nrefs +1)); this->refs[this->nrefs] = refd_he->ol; this->nrefs++; } return OK; } /* flag values */ #define M_MAKE 1 #define M_MARK 2 /* traverse the objectlist, and collect named objects into a group to * return. if namelist isn't null, names must be in list to be returned. */ Object _dxfnamedobjlist(struct finfo *f, char **namelist) { return namedobjfunc(f, namelist, M_MAKE); } /* traverse the objectlist and just mark the objects in the namelist as * used. then mark all the objects they use. */ Object _dxfmarknamedobjlist(struct finfo *f, char **namelist) { return namedobjfunc(f, namelist, M_MARK); } /* common code for both functions above. */ static Object namedobjfunc(struct finfo *f, char **namelist, int flag) { struct objlist *this, *first = NULL; struct objlist *ref; Object o = NULL; int total = 0; int members = 0; int named = 0; int i, matched; char **cp; if (!f || !f->ol) return ERROR; /* traverse the list, keeping any object which matches the namelist. * when you find the second match, create a group to hold the objects * and return it at the end. if there is only one match, return it. */ for (this=f->ol; this; this = this->next) { /* count total number of objects */ total++; /* skipped unnamed objects */ if (!this->name) continue; named++; if (!namelist || !namelist[0]) matched = 1; else { matched = 0; for (cp=namelist; *cp; cp++) { if (strcmp(*cp, this->name)) continue; matched = 1; break; } } if (!matched) continue; this->oused = OBJ_USED; /* are we really making objects or just marking them used? */ if (flag == M_MAKE) { /* mark it nodelete until we decide if it is going to be * the toplevel (or only) object returned. */ this->odelete = OBJ_NODELETE; /* if this is the second match, make a group and put the first * member in it. */ if (first && !o) { o = (Object)DXNewGroup(); if (!o) return ERROR; o = (Object)DXSetMember((Group)o, first->name, first->o); if (!o) return ERROR; first->odelete = OBJ_CANDELETE; } /* if this is the first object, remember it, else add it to group. */ if (!first) { first = this; } else { o = (Object)DXSetMember((Group)o, this->name, this->o); if (!o) return ERROR; this->odelete = OBJ_CANDELETE; } } else { /* if just marking, still remember the first object marked */ if (!first) first = this; } members++; } /* if we are marking objects used, mark each refd object used also * * can we cheat here because objects are added to head of the list * so all references should only be forward? (yes for now) */ if (flag == M_MARK) { for (this=f->ol; this; this = this->next) { if (this->oused != OBJ_USED) continue; for (i = 0; i < this->nrefs; i++) this->refs[i]->oused = OBJ_USED; } } /* either no named objects, or no matches with list. the exception * is if there is only a single object in the file - then return it. */ if (members == 0) { if (total == 1) { this = f->ol; if (M_MARK) this->oused = OBJ_USED; else this->odelete = OBJ_NODELETE; return this->o; } if (!named) { DXSetError(ERROR_INVALID_DATA, "#10740"); return NULL; } else { DXSetError(ERROR_INVALID_DATA, "#10742"); return NULL; } } /* if exactly one object found, return it; else return group. */ if (members == 1) return first->o; return o; } /* traverse the objectlist, and collect the requested object numbers into * a group to return. unlike _dxfnamedobjlist(), if numlist is null, don't * try to return all numbered objects, just return NULL. if idflag = 0, * use user numbers. if idflag = 1, use the real id numbers. if idflag * = 2, just return the first object in the list, which was the last * object defined from this file. */ Object _dxfnumberedobjlist(struct finfo *f, int *numlist, int idflag) { return numberedobjfunc(f, numlist, idflag, M_MAKE); } /* same as above but only mark the used obj numbers and then mark * the objects they use. */ Object _dxfmarknumberedobjlist(struct finfo *f, int *numlist, int idflag) { return numberedobjfunc(f, numlist, idflag, M_MARK); } /* common code for both functions above. */ static Object numberedobjfunc(struct finfo *f, int *numlist, int idflag, int flag) { struct objlist *this, *first = NULL; Object o = NULL; int members = 0; int named = 0; int i, matched; int *ip; if (!f || !f->ol) return ERROR; /* idflag == 2 means mark/return only most recently defined object * if you aren't asking for the last object, you have to give a valid * number list. */ if (idflag == 2) { this = f->ol; if (flag == M_MARK) { this->oused = OBJ_USED; members = 1; first = this; /* continue down to the section where we mark all the * objects referenced by this one. */ } else { /* return here - we are done */ this->odelete = OBJ_NODELETE; return this->o; } } else { if (!numlist) return ERROR; /* traverse the list, assuming the return object will be a group. * when you get to the end, if there is only one member in the group, * delete the group and return the single member. */ for (this=f->ol; this; this = this->next) { if (this->name) named++; matched = 0; for (ip=numlist; *ip >= 0; ip++) { if ((!idflag && ((USER_ID(*ip) != this->id))) || (idflag && (*ip != this->id))) continue; matched = 1; break; } if (!matched) continue; this->oused = OBJ_USED; /* are we really making objects or just marking them used? */ if (flag == M_MAKE) { /* mark it nodelete until we decide if it is going to be * the toplevel (or only) object returned. */ this->odelete = OBJ_NODELETE; /* if this is the second match, make a group and put the first * member in it. */ if (first && !o) { o = (Object)DXNewGroup(); if (!o) return ERROR; o = (Object)DXSetMember((Group)o, first->name, first->o); if (!o) return ERROR; first->odelete = OBJ_CANDELETE; } /* if the first object, remember it, else add it to group. */ if (!first) { first = this; } else { o = (Object)DXSetMember((Group)o, this->name, this->o); if (!o) return ERROR; this->odelete = OBJ_CANDELETE; } } else { /* if just marking, still remember the first object marked */ if (!first) first = this; } members++; } } /* if we are marking objects used, mark each refd object used also. * * can we cheat here because objects are added to head of the list * so all references should only be forward? (yes for now) */ if (flag == M_MARK) { for (this=f->ol; this; this = this->next) { if (this->oused != OBJ_USED) continue; for (i = 0; i < this->nrefs; i++) this->refs[i]->oused = OBJ_USED; } } /* either no named objects, or no matches with list */ if (members == 0) { DXSetError(ERROR_INVALID_DATA, "#10746"); return NULL; } /* if exactly one object found, return it; else return group. */ if (members == 1) return first->o; return o; } /* for debug, dump object list */ static void printobjlist(struct finfo *f) { struct objlist *this; for (this=f->ol; this; this = this->next) { DXMessage("id=%d, obj=%x, name=%s", this->id, this->o, this->name); DXMessage(" used=%d, del=%d, nrefs=%d", this->oused, this->odelete, this->nrefs); } } /* * hash on object and return id */ static struct hashobjtoid { Object o; int id; int literal; }; /* init list - create hash table. */ Error _dxfinit_objident(HashTable *ht) { if (!ht) return ERROR; *ht = DXCreateHash(sizeof(struct hashobjtoid), NULL, NULL); if (!*ht) return ERROR; return OK; } /* delete list */ Error _dxfdelete_objident(HashTable ht) { int rc; if (!ht) return ERROR; rc = DXDestroyHash(ht); return rc; } /* add a new object to the hash table. * inputs are the object, the associated id, and a flag to say whether * this is a literal number or a dictionary id. */ Error _dxfadd_objident(HashTable ht, Object o, int id, int literal) { struct hashobjtoid he; if (!ht || !o) return ERROR; /* add to hash table for quick lookup */ he.o = o; he.id = id; he.literal = literal; if (!DXInsertHashElement(ht, (Pointer)&he)) return ERROR; if (DXQueryDebug("O")) { DXDebug("O", "added object 0x%08x, %s %d", o, literal ? "number" : "dictionary string", id); } return OK; } /* return the id associated with an object if it is already in the table. * if not found, return NULL id. only return error if something bad happens. */ Error _dxflook_objident(HashTable ht, Object o, int *id, int *literal) { struct hashobjtoid *he; if (!ht || !o || !id || !literal) return ERROR; /* check for object in hash table. */ he = (struct hashobjtoid *)DXQueryHashElement(ht, (Key)&o); if (!he) *id = NULL; else { *id = he->id; *literal = he->literal; } return OK; }