/***********************************************************************/ /* 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" */ /***********************************************************************/ /* * (C) COPYRIGHT International Business Machines Corp. 1997 * All Rights Reserved * Licensed Materials - Property of IBM * * US Government Users Restricted Rights - Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ #include #if defined(HAVE_STRING_H) #include #endif #include #include #include "changemember.h" /* if we made this "changemember" it could be * made to work on lists as well as groups. * would be a good compliment to Select(). * but we didn't make Append do lists - i made * a List module because one module didn't fit * the input parms exactly. */ /* operations, relative to an existing member */ #define O_UNKNOWN 0 #define O_INSERT 1 #define O_INSERT_BEFORE 2 #define O_REPLACE 3 #define O_INSERT_AFTER 4 #define O_DELETE 5 static Error do_operation(Group newgrp, char *memname, int id, int nmembers, Class grouptype, int optype, Object newmember, float newseriespos, char *newmemname, int byname); /* inputs: existing group, * operation (insert before, replace, insert after, delete), * existing member id, * newmember, newtag (seriespos or name) * outputs: copy of group with changed member */ Error m_ChangeGroupMember(Object *in, Object *out) { int nmembers; int id; char *cp; char *opname = NULL; int optype = O_UNKNOWN; char *memname = NULL; char *newmemname = NULL; int byname = 0; Class grouptype; float newseriespos; Group newgrp = NULL; Object newmember = NULL; /* first input must be an existing group. required. */ if (!in[0] || DXGetObjectClass(in[0]) != CLASS_GROUP) { /* must be a group */ DXSetError(ERROR_BAD_PARAMETER, "#10192", "input"); return ERROR; } grouptype = DXGetGroupClass((Group)in[0]); DXGetMemberCount((Group)in[0], &nmembers); /* what to do - insert/replace/delete. required. */ if (!in[1] || !DXExtractString(in[1], &cp)) { DXSetError(ERROR_BAD_PARAMETER, "`operation' must be specified and one of `insert', `replace', `delete', or `insert before' and `insert after' for series groups'"); goto error; } /* lcase string and squish spaces here */ opname = dxf_lsquish(cp); if (!opname) goto error; if (!strcmp(opname, "insert")) optype = O_INSERT; else if (!strcmp(opname, "insertbefore")) optype = O_INSERT_BEFORE; else if (!strcmp(opname, "replace")) optype = O_REPLACE; else if (!strcmp(opname, "insertafter")) optype = O_INSERT_AFTER; else if (!strcmp(opname, "delete")) optype = O_DELETE; else { DXSetError(ERROR_BAD_PARAMETER, "`operation' must be one of `insert', `replace', `delete' or `insert before' or `insert after'"); DXFree((Pointer)opname); goto error; } DXFree((Pointer)opname); /* ok, everything from here on down is in most ways dependent on the * type of operation requested. */ /* make sure insert before/after only used w/series */ if ((optype == O_INSERT_BEFORE || optype == O_INSERT_AFTER) && (grouptype != CLASS_SERIES)) { DXSetError(ERROR_BAD_PARAMETER, "`operation' cannot be `insert before' or `insert after' unless input is a series group; use `insert' instead"); goto error; } /* if operation != delete, user has to specify new member. otherwise * they can't specify new member. i suppose that for replace it can * be interpreted that replace with no new member means change the * series position or name of specified existing member. */ if (optype == O_DELETE) { if (in[3]) { DXSetError(ERROR_BAD_PARAMETER, "cannot specify new member for `delete' operation"); goto error; } } else if (optype == O_REPLACE) { ; /* don't care whether new member spec'd or not */ } else { if (!in[3]) { DXSetError(ERROR_BAD_PARAMETER, "`newmember' object must be specified"); goto error; } } newmember = in[3]; /* this might be null here and that's ok */ /* existing member id - by ordinal or name */ if (!in[2]) { if (optype != O_INSERT) { /* doesn't require an existing id */ DXSetError(ERROR_BAD_PARAMETER, "`id' must be either integer index or string name"); goto error; } id = nmembers-1; /* set this in case someone cares - add at end */ } else { if (!DXExtractInteger(in[2], &id) && !DXExtractString(in[2], &memname)) { DXSetError(ERROR_BAD_PARAMETER, "`id' must be either integer index or string name"); goto error; } if (optype == O_INSERT) { DXSetError(ERROR_BAD_PARAMETER, "cannot specify `id' parameter for insert"); goto error; } } if (memname != NULL) byname++; /* if by ordinal - check the value is in range */ if (!byname && (id < 0 || id >= nmembers)) { DXSetError(ERROR_BAD_PARAMETER, "group has %d members, id (%d) must be between 0 and %d", nmembers, id, nmembers-1); goto error; } /* if by name, make sure member exists */ if (byname && !DXGetMember((Group)in[0], memname)) { DXSetError(ERROR_BAD_PARAMETER, "#11320", memname); /* group has no members with name `%s' */ goto error; } /* this is a strange combination - cut this off here */ if (byname && grouptype == CLASS_SERIES) { DXSetError(ERROR_DATA_INVALID, "cannot get/set a series member by name"); goto error; } /* this seems like it will be needed later on - the id even if the member * is specified by name. if byname, look up the member number anyway * so "id" has a valid value. ditto for id - see if there is a name * and/or seriespos. */ if (byname) { for (id=0; DXGetEnumeratedMember((Group)in[0], id, &cp); id++) { if (!strcmp(cp, memname)) break; } if (id == nmembers) { /* shouldn't happen - we looked it up by name before and shouldn't * have gotten here if we found it before. */ DXSetError(ERROR_INTERNAL, "cannot find member by name"); goto error; } } else { if (!DXGetEnumeratedMember((Group)in[0], id, &memname)) { DXSetError(ERROR_INTERNAL, "cannot find member by id"); goto error; } if (grouptype == CLASS_SERIES) { if (!DXGetSeriesMember((Series)in[0], id, &newseriespos)) goto error; } } /* newid optional for replace, not permitted for delete. * for insert, required for series and optional for all others. */ if (optype == O_DELETE) { if (in[4]) { DXSetError(ERROR_BAD_PARAMETER, "cannot specify `newtag' for delete operation"); goto error; } } else if (optype == O_REPLACE) { /* split this into two separate tests for better error msgs */ if (grouptype == CLASS_SERIES) { if (in[4]) { if (!DXExtractFloat(in[4], &newseriespos)) { DXSetError(ERROR_BAD_PARAMETER, "`newtag' must be a floating point series position"); goto error; } } else if (! DXGetSeriesMember((Series)in[0], id, &newseriespos)) { DXSetError(ERROR_BAD_PARAMETER, "Input has no member %d", id); goto error; } } else { if (in[4]) { if (!DXExtractString(in[4], &newmemname)) { DXSetError(ERROR_BAD_PARAMETER, "`newtag' must be a string name"); goto error; } } else newmemname = memname; } } else { if (grouptype == CLASS_SERIES) { if (!in[4] || !DXExtractFloat(in[4], &newseriespos)) { DXSetError(ERROR_BAD_PARAMETER, "`newtag' must be a floating point series position"); goto error; } } else { if (in[4] && !DXExtractString(in[4], &newmemname)) { DXSetError(ERROR_BAD_PARAMETER, "`newtag' must be a string name"); goto error; } } } /* now be sure that for replace that at least one of newmember or * newtag was set. */ if (optype == O_REPLACE && newmember == NULL && !in[4]) { DXSetError(ERROR_BAD_PARAMETER, "`replace' operation requires at least one of `newmember' or `newtag' be specified"); goto error; } /* make sure that replace of existing member specified by id * doesn't have a name collision. same with insert - verify * there isn't already a member with this name. */ if (optype == O_REPLACE && !byname && newmemname) { if (DXGetMember((Group)in[0], newmemname) != NULL) { DXSetError(ERROR_BAD_PARAMETER, "group has another member with name '%s' not at index %d", newmemname, id); goto error; } } if (optype == O_INSERT && newmemname) { if (DXGetMember((Group)in[0], newmemname) != NULL) { DXSetError(ERROR_BAD_PARAMETER, "group already has a member with name `%s'", newmemname); goto error; } } /* ok, we have all the input parms parsed at this point. */ /* make copy of top level object, keeping ptrs to members */ newgrp = (Group)DXCopy(in[0], COPY_HEADER); if (!newgrp) goto error; if (do_operation(newgrp, memname, id, nmembers, grouptype, optype, newmember, newseriespos, newmemname, byname) == ERROR) goto error; out[0] = (Object)newgrp; return OK; error: DXDelete((Object)newgrp); out[0] = NULL; return ERROR; } static Error do_operation(Group newgrp, char *memname, int id, int nmembers, Class grouptype, int optype, Object newmember, float newseriespos, char *newmemname, int byname) { int i; char *oldname; float seriespos; Object subo; /* do operation specific code */ switch (optype) { case O_DELETE: if (byname) { if (!DXSetMember(newgrp, memname, NULL)) /* does this del? */ goto error; } else { if (!DXSetEnumeratedMember(newgrp, id, NULL)) /* ditto */ goto error; } break; case O_INSERT: if (grouptype != CLASS_SERIES) { if (!DXSetMember(newgrp, newmemname, newmember)) goto error; } else { /* series - should this be ok? do an append. */ if (!DXSetSeriesMember((Series)newgrp, nmembers, newseriespos, newmember)) goto error; } break; case O_INSERT_BEFORE: /* already verified input is series */ for (i=nmembers-1; i >= id; --i) { subo = DXGetSeriesMember((Series)newgrp, i, &seriespos); if (!subo || !DXSetSeriesMember((Series)newgrp, i+1, seriespos, subo)) goto error; } if (!DXSetSeriesMember((Series)newgrp, id, newseriespos, newmember)) goto error; break; case O_INSERT_AFTER: /* already verified input is series */ for (i=nmembers-1; i > id; --i) { subo = DXGetSeriesMember((Series)newgrp, i, &seriespos); if (!subo || !DXSetSeriesMember((Series)newgrp, i+1, seriespos, subo)) goto error; } if (!DXSetSeriesMember((Series)newgrp, id+1, newseriespos, newmember)) goto error; break; case O_REPLACE: /* two main cases - a new member is specified (the usual use), * and the new member is not specified (meaning you want to change * either the name or series position tag of an existing member). */ if (newmember != NULL) { /* the "normal" case */ /* 4 cases here - byname & byid (!byname) into a series & !series * except that byname into series doesn't make sense and has * already been checked for and excluded. */ if (!byname && grouptype == CLASS_SERIES) { /* replace by id into a series */ if (!DXSetSeriesMember((Series)newgrp, id, newseriespos, newmember)) goto error; } else if (!byname && grouptype != CLASS_SERIES) { /* replace by id into non-series */ /* add new member by name, then del old by id */ if (!DXSetMember(newgrp, newmemname, newmember)) goto error; if (!DXSetEnumeratedMember(newgrp, id, NULL)) goto error; } else if (byname && grouptype != CLASS_SERIES) { /* replace by name into non-series */ /* add new member by name, then del old by name if !same */ if (!DXSetMember(newgrp, newmemname, newmember)) goto error; if (strcmp(memname, newmemname)) if (!DXSetMember(newgrp, memname, NULL)) goto error; } else { ; /* byname & type == series. "can't happen" */ } } else { /* newmember == NULL; just rename or set new seriespos */ /* 4 cases here - byname & byid (!byname) into a series & !series * except that byname into series doesn't make sense and has * already been checked for and excluded. */ if (!byname && grouptype == CLASS_SERIES) { /* change seriespos by id */ if (!(newmember = DXGetSeriesMember((Series)newgrp, id, &seriespos))) goto error; if (!DXSetSeriesMember((Series)newgrp, id, newseriespos, newmember)) goto error; } else if (!byname && grouptype != CLASS_SERIES) { /* change name by id into non-series */ /* get old member by id, add new member by name, * then del old by id */ if (!(newmember = DXGetEnumeratedMember(newgrp, id, &oldname))) goto error; if (!DXSetMember(newgrp, newmemname, newmember)) goto error; if (!DXSetEnumeratedMember(newgrp, id, NULL)) goto error; } else if (byname && grouptype != CLASS_SERIES) { /* change by name into non-series */ /* get the member, set it under the new name, and then * del it with the old name. don't do it in a different * order or you might end up losing it if the ref count * goes to 0. */ if (!(newmember = DXGetMember(newgrp, memname))) goto error; if (!DXSetMember(newgrp, newmemname, newmember)) goto error; if (!DXSetMember(newgrp, memname, NULL)) goto error; } else { ; /* byname & type == series. "can't happen" */ } } break; } return OK; error: return ERROR; } /* this routines allocates new space and makes a copy of the * input string. the caller has to free the space when done. * all chars are converted to lower case and blanks are removed. */ char *dxf_lsquish(char *instr) { char *outstr = NULL; char *cpi, *cpo; outstr = (char *)DXAllocate(strlen(instr) + 1); if (!outstr) return NULL; for (cpi=instr, cpo=outstr; *cpi; cpi++) { if (isspace(*cpi)) continue; if (isupper(*cpi)) *cpo++ = tolower(*cpi); else *cpo++ = *cpi; } *cpo = *cpi; return outstr; }