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