1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 /*
9  * (C) COPYRIGHT International Business Machines Corp. 1997
10  * All Rights Reserved
11  * Licensed Materials - Property of IBM
12  *
13  * US Government Users Restricted Rights - Use, duplication or
14  * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
15  */
16 
17 #include <dxconfig.h>
18 
19 #if defined(HAVE_STRING_H)
20 #include <string.h>
21 #endif
22 
23 #include <dx/dx.h>
24 #include <ctype.h>
25 #include "changemember.h"
26 
27 /* if we made this "changemember" it could be
28  *  made to work on lists as well as groups.
29  *  would be a good compliment to Select().
30  *  but we didn't make Append do lists - i made
31  *  a List module because one module didn't fit
32  *  the input parms exactly.
33  */
34 
35 /* operations, relative to an existing member */
36 #define O_UNKNOWN        0
37 #define O_INSERT         1
38 #define O_INSERT_BEFORE  2
39 #define O_REPLACE        3
40 #define O_INSERT_AFTER   4
41 #define O_DELETE         5
42 
43 static Error do_operation(Group newgrp, char *memname, int id, int nmembers,
44 			  Class grouptype, int optype, Object newmember,
45 			  float newseriespos, char *newmemname, int byname);
46 
47 
48 /* inputs: existing group,
49  *          operation (insert before, replace, insert after, delete),
50  *          existing member id,
51  *          newmember, newtag (seriespos or name)
52  * outputs: copy of group with changed member
53  */
m_ChangeGroupMember(Object * in,Object * out)54 Error m_ChangeGroupMember(Object *in, Object *out)
55 {
56     int nmembers;
57     int id;
58     char *cp;
59     char *opname = NULL;
60     int optype = O_UNKNOWN;
61     char *memname = NULL;
62     char *newmemname = NULL;
63     int byname = 0;
64     Class grouptype;
65     float newseriespos;
66     Group newgrp = NULL;
67     Object newmember = NULL;
68 
69 
70 
71     /* first input must be an existing group.  required.
72      */
73     if (!in[0] || DXGetObjectClass(in[0]) != CLASS_GROUP) {
74         /* must be a group */
75         DXSetError(ERROR_BAD_PARAMETER, "#10192", "input");
76         return ERROR;
77     }
78     grouptype = DXGetGroupClass((Group)in[0]);
79     DXGetMemberCount((Group)in[0], &nmembers);
80 
81 
82 
83     /* what to do - insert/replace/delete.  required.
84      */
85     if (!in[1] || !DXExtractString(in[1], &cp)) {
86         DXSetError(ERROR_BAD_PARAMETER,
87 		   "`operation' must be specified and one of `insert', `replace', `delete', or `insert before' and `insert after' for series groups'");
88         goto error;
89     }
90     /* lcase string and squish spaces here */
91     opname = dxf_lsquish(cp);
92     if (!opname)
93         goto error;
94 
95     if (!strcmp(opname, "insert"))
96         optype = O_INSERT;
97     else if (!strcmp(opname, "insertbefore"))
98         optype = O_INSERT_BEFORE;
99     else if (!strcmp(opname, "replace"))
100         optype = O_REPLACE;
101     else if (!strcmp(opname, "insertafter"))
102         optype = O_INSERT_AFTER;
103     else if (!strcmp(opname, "delete"))
104         optype = O_DELETE;
105     else {
106         DXSetError(ERROR_BAD_PARAMETER,
107 		   "`operation' must be one of `insert', `replace', `delete' or `insert before' or `insert after'");
108 	DXFree((Pointer)opname);
109         goto error;
110     }
111     DXFree((Pointer)opname);
112 
113 
114     /* ok, everything from here on down is in most ways dependent on the
115      * type of operation requested.
116      */
117 
118 
119     /* make sure insert before/after only used w/series */
120     if ((optype == O_INSERT_BEFORE || optype == O_INSERT_AFTER) &&
121 	(grouptype != CLASS_SERIES)) {
122 	DXSetError(ERROR_BAD_PARAMETER,
123 		   "`operation' cannot be `insert before' or `insert after' unless input is a series group; use `insert' instead");
124 	goto error;
125     }
126 
127 
128     /* if operation != delete, user has to specify new member.  otherwise
129      * they can't specify new member.  i suppose that for replace it can
130      * be interpreted that replace with no new member means change the
131      * series position or name of specified existing member.
132      */
133     if (optype == O_DELETE) {
134         if (in[3]) {
135             DXSetError(ERROR_BAD_PARAMETER,
136 		       "cannot specify new member for `delete' operation");
137             goto error;
138         }
139     } else if (optype == O_REPLACE) {
140         ; /* don't care whether new member spec'd or not */
141     } else {
142         if (!in[3]) {
143             DXSetError(ERROR_BAD_PARAMETER, "`newmember' object must be specified");
144             goto error;
145         }
146     }
147     newmember = in[3];  /* this might be null here and that's ok */
148 
149 
150     /* existing member id - by ordinal or name
151      */
152     if (!in[2]) {
153 	if (optype != O_INSERT) {  /* doesn't require an existing id */
154 	    DXSetError(ERROR_BAD_PARAMETER,
155 		       "`id' must be either integer index or string name");
156 	    goto error;
157 	}
158 	id = nmembers-1;   /* set this in case someone cares - add at end */
159 
160     } else {
161 	if (!DXExtractInteger(in[2], &id) && !DXExtractString(in[2], &memname)) {
162 	    DXSetError(ERROR_BAD_PARAMETER,
163 		       "`id' must be either integer index or string name");
164 	    goto error;
165 	}
166 	if (optype == O_INSERT) {
167 	    DXSetError(ERROR_BAD_PARAMETER, "cannot specify `id' parameter for insert");
168 	    goto error;
169 	}
170     }
171 
172     if (memname != NULL)
173 	byname++;
174 
175 
176     /* if by ordinal - check the value is in range */
177     if (!byname && (id < 0 || id >= nmembers)) {
178 	DXSetError(ERROR_BAD_PARAMETER,
179 		   "group has %d members, id (%d) must be between 0 and %d",
180 		   nmembers, id, nmembers-1);
181 	goto error;
182     }
183 
184     /* if by name, make sure member exists */
185     if (byname && !DXGetMember((Group)in[0], memname)) {
186 	DXSetError(ERROR_BAD_PARAMETER, "#11320", memname);
187 	/* group has no members with name `%s' */
188 	goto error;
189     }
190 
191     /* this is a strange combination - cut this off here */
192     if (byname && grouptype == CLASS_SERIES) {
193 	DXSetError(ERROR_DATA_INVALID, "cannot get/set a series member by name");
194 	goto error;
195     }
196 
197     /* this seems like it will be needed later on - the id even if the member
198      * is specified by name.  if byname, look up the member number anyway
199      * so "id" has a valid value.  ditto for id - see if there is a name
200      * and/or seriespos.
201      */
202     if (byname) {
203         for (id=0; DXGetEnumeratedMember((Group)in[0], id, &cp); id++) {
204             if (!strcmp(cp, memname))
205                 break;
206         }
207         if (id == nmembers) {
208             /* shouldn't happen - we looked it up by name before and shouldn't
209              * have gotten here if we found it before.
210              */
211             DXSetError(ERROR_INTERNAL, "cannot find member by name");
212             goto error;
213         }
214     } else {
215 	if (!DXGetEnumeratedMember((Group)in[0], id, &memname)) {
216             DXSetError(ERROR_INTERNAL, "cannot find member by id");
217 	    goto error;
218 	}
219 	if (grouptype == CLASS_SERIES) {
220 	    if (!DXGetSeriesMember((Series)in[0], id, &newseriespos))
221 		goto error;
222 	}
223    }
224 
225 
226 
227 
228     /* newid optional for replace, not permitted for delete.
229      * for insert, required for series and optional for all others.
230      */
231     if (optype == O_DELETE) {
232         if (in[4]) {
233             DXSetError(ERROR_BAD_PARAMETER, "cannot specify `newtag' for delete operation");
234             goto error;
235         }
236     } else if (optype == O_REPLACE) {
237         /* split this into two separate tests for better error msgs */
238 
239         if (grouptype == CLASS_SERIES)
240 	{
241 	    if (in[4])
242 	    {
243 		if (!DXExtractFloat(in[4], &newseriespos))
244 		{
245 		    DXSetError(ERROR_BAD_PARAMETER,
246 			       "`newtag' must be a floating point series position");
247 		    goto error;
248 		}
249 	    }
250 	    else if (! DXGetSeriesMember((Series)in[0], id, &newseriespos))
251 	    {
252 	        DXSetError(ERROR_BAD_PARAMETER, "Input has no member %d", id);
253 		goto error;
254 	    }
255 	}
256 	else
257 	{
258 	    if (in[4])
259 	    {
260 		if (!DXExtractString(in[4], &newmemname))
261 		{
262 		    DXSetError(ERROR_BAD_PARAMETER, "`newtag' must be a string name");
263 		    goto error;
264 		}
265             }
266 	    else
267 	        newmemname = memname;
268 	}
269 
270     } else {
271         if (grouptype == CLASS_SERIES) {
272             if (!in[4] || !DXExtractFloat(in[4], &newseriespos)) {
273                 DXSetError(ERROR_BAD_PARAMETER,
274                    "`newtag' must be a floating point series position");
275                 goto error;
276             }
277         } else {
278             if (in[4] && !DXExtractString(in[4], &newmemname)) {
279                 DXSetError(ERROR_BAD_PARAMETER,
280                     "`newtag' must be a string name");
281                 goto error;
282             }
283         }
284     }
285 
286 
287     /* now be sure that for replace that at least one of newmember or
288      * newtag was set.
289      */
290     if (optype == O_REPLACE && newmember == NULL && !in[4]) {
291         DXSetError(ERROR_BAD_PARAMETER,
292          "`replace' operation requires at least one of `newmember' or `newtag' be specified");
293         goto error;
294     }
295 
296     /* make sure that replace of existing member specified by id
297      * doesn't have a name collision.  same with insert - verify
298      * there isn't already a member with this name.
299      */
300     if (optype == O_REPLACE && !byname && newmemname) {
301 	if (DXGetMember((Group)in[0], newmemname) != NULL) {
302 	    DXSetError(ERROR_BAD_PARAMETER, "group has another member with name '%s' not at index %d", newmemname, id);
303 	    goto error;
304 	}
305     }
306     if (optype == O_INSERT && newmemname) {
307 	if (DXGetMember((Group)in[0], newmemname) != NULL) {
308 	    DXSetError(ERROR_BAD_PARAMETER,
309 		       "group already has a member with name `%s'", newmemname);
310 	    goto error;
311 	}
312     }
313 
314 
315     /* ok, we have all the input parms parsed at this point. */
316 
317 
318     /* make copy of top level object, keeping ptrs to members
319      */
320     newgrp = (Group)DXCopy(in[0], COPY_HEADER);
321     if (!newgrp)
322         goto error;
323 
324 
325     if (do_operation(newgrp, memname, id, nmembers, grouptype, optype,
326 		     newmember, newseriespos, newmemname, byname) == ERROR)
327 	goto error;
328 
329     out[0] = (Object)newgrp;
330     return OK;
331 
332   error:
333     DXDelete((Object)newgrp);
334     out[0] = NULL;
335     return ERROR;
336 }
337 
338 
339 
do_operation(Group newgrp,char * memname,int id,int nmembers,Class grouptype,int optype,Object newmember,float newseriespos,char * newmemname,int byname)340 static Error do_operation(Group newgrp, char *memname, int id, int nmembers,
341 			  Class grouptype, int optype, Object newmember,
342 			  float newseriespos, char *newmemname, int byname)
343 {
344     int i;
345     char *oldname;
346     float seriespos;
347     Object subo;
348 
349 
350 
351     /* do operation specific code */
352     switch (optype) {
353 
354       case O_DELETE:
355         if (byname) {
356             if (!DXSetMember(newgrp, memname, NULL))  /* does this del? */
357                 goto error;
358         } else {
359             if (!DXSetEnumeratedMember(newgrp, id, NULL))  /* ditto */
360                 goto error;
361         }
362 	break;
363 
364 
365       case O_INSERT:
366 	if (grouptype != CLASS_SERIES) {
367 	    if (!DXSetMember(newgrp, newmemname, newmember))
368 		goto error;
369         } else {   /* series - should this be ok? do an append. */
370 	    if (!DXSetSeriesMember((Series)newgrp, nmembers,
371 				   newseriespos, newmember))
372 		goto error;
373 	}
374         break;
375 
376 
377       case O_INSERT_BEFORE:   /* already verified input is series */
378 	for (i=nmembers-1; i >= id; --i) {
379 	    subo = DXGetSeriesMember((Series)newgrp, i, &seriespos);
380 	    if (!subo || !DXSetSeriesMember((Series)newgrp, i+1, seriespos, subo))
381 		goto error;
382 	}
383 	if (!DXSetSeriesMember((Series)newgrp, id, newseriespos, newmember))
384 	    goto error;
385 
386         break;
387 
388 
389       case O_INSERT_AFTER:   /* already verified input is series */
390 	for (i=nmembers-1; i > id; --i) {
391 	    subo = DXGetSeriesMember((Series)newgrp, i, &seriespos);
392 	    if (!subo || !DXSetSeriesMember((Series)newgrp, i+1, seriespos, subo))
393 		goto error;
394 	}
395 	if (!DXSetSeriesMember((Series)newgrp, id+1, newseriespos, newmember))
396 	    goto error;
397 
398         break;
399 
400 
401       case O_REPLACE:
402         /* two main cases - a new member is specified (the usual use),
403 	 * and the new member is not specified (meaning you want to change
404 	 * either the name or series position tag of an existing member).
405          */
406 
407         if (newmember != NULL) {  /* the "normal" case */
408 
409 	    /* 4 cases here - byname & byid (!byname) into a series & !series
410 	     *  except that byname into series doesn't make sense and has
411 	     *  already been checked for and excluded.
412 	     */
413 	    if (!byname && grouptype == CLASS_SERIES) {
414 		/* replace by id into a series */
415 		if (!DXSetSeriesMember((Series)newgrp, id, newseriespos, newmember))
416 		    goto error;
417 	    } else if (!byname && grouptype != CLASS_SERIES) {
418 		/* replace by id into non-series */
419 		/* add new member by name, then del old by id */
420 		if (!DXSetMember(newgrp, newmemname, newmember))
421 		    goto error;
422 		if (!DXSetEnumeratedMember(newgrp, id, NULL))
423 		    goto error;
424 	    } else if (byname && grouptype != CLASS_SERIES) {
425 		/* replace by name into non-series */
426 		/* add new member by name, then del old by name if !same */
427 		if (!DXSetMember(newgrp, newmemname, newmember))
428 		    goto error;
429 		if (strcmp(memname, newmemname))
430 		    if (!DXSetMember(newgrp, memname, NULL))
431 			goto error;
432 	    } else {
433 		; /* byname & type == series.  "can't happen" */
434 	    }
435 
436 	} else {  /* newmember == NULL; just rename or set new seriespos */
437 
438 	    /* 4 cases here - byname & byid (!byname) into a series & !series
439 	     *  except that byname into series doesn't make sense and has
440 	     *  already been checked for and excluded.
441 	     */
442 	    if (!byname && grouptype == CLASS_SERIES) {
443 		/* change seriespos by id */
444 		if (!(newmember = DXGetSeriesMember((Series)newgrp, id, &seriespos)))
445 		    goto error;
446 		if (!DXSetSeriesMember((Series)newgrp, id, newseriespos, newmember))
447 		    goto error;
448 
449 	    } else if (!byname && grouptype != CLASS_SERIES) {
450 		/* change name by id into non-series */
451 		/* get old member by id, add new member by name,
452 		 *  then del old by id
453 		 */
454 		if (!(newmember = DXGetEnumeratedMember(newgrp, id, &oldname)))
455 		    goto error;
456 		if (!DXSetMember(newgrp, newmemname, newmember))
457 		    goto error;
458 		if (!DXSetEnumeratedMember(newgrp, id, NULL))
459 		    goto error;
460 
461 	    } else if (byname && grouptype != CLASS_SERIES) {
462 		/* change by name into non-series */
463 		/* get the member, set it under the new name, and then
464 		 * del it with the old name.  don't do it in a different
465 		 * order or you might end up losing it if the ref count
466 		 * goes to 0.
467 		 */
468                 if (!(newmember = DXGetMember(newgrp, memname)))
469 		    goto error;
470 		if (!DXSetMember(newgrp, newmemname, newmember))
471 		    goto error;
472 		if (!DXSetMember(newgrp, memname, NULL))
473 		    goto error;
474 
475 	    } else {
476 		; /* byname & type == series.  "can't happen" */
477 	    }
478 	}
479         break;
480     }
481 
482     return OK;
483 
484   error:
485     return ERROR;
486 }
487 
488 
489 /* this routines allocates new space and makes a copy of the
490  * input string.  the caller has to free the space when done.
491  * all chars are converted to lower case and blanks are removed.
492  */
dxf_lsquish(char * instr)493 char *dxf_lsquish(char *instr)
494 {
495     char *outstr = NULL;
496     char *cpi, *cpo;
497 
498     outstr = (char *)DXAllocate(strlen(instr) + 1);
499     if (!outstr)
500         return NULL;
501 
502     for (cpi=instr, cpo=outstr; *cpi; cpi++) {
503 
504 	if (isspace(*cpi))
505 	    continue;
506 
507 	if (isupper(*cpi))
508 	    *cpo++ = tolower(*cpi);
509 	else
510 	    *cpo++ = *cpi;
511     }
512 
513     *cpo = *cpi;
514     return outstr;
515 }
516