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 #include <dxconfig.h>
10
11
12
13 /*
14 * Process Parts
15 * John E. Allain
16 * Apply a user specified function to each part of a group.
17 * assemble groups with the same structure as the input.
18 */
19
20 #include <string.h>
21 #include <dx/dx.h>
22
23
24 #define PartsDebugChar "P"
25 #define ErrorGotoPlus1(e,s,a) {DXSetError(e,s,a); goto error;}
26 #define MessageGotoPlus1(s,a) {DXAddMessage(s,a); goto error;}
27
28 /*
29 * Running debate:
30 * The structure of _recurse is convolved because the convention
31 * 'copy' passes fields uncopied to the process_part worker.
32 * If it were copied first, and the worker modified components in
33 * place, then it could be cleaner. This would however require the
34 * worker to clean out beaucoup components when it really wants to
35 * build a new field.
36 *
37 * Note significant changes:
38 * DXEmptyField check is no longer performed.
39 * The DXEndField call is now expected of the worker routine.
40 *
41 * Function naming convention:
42 *
43 * Mixed case lettering, leading underscore:
44 * public (extern) routine, undocumented.
45 * Mixed case lettering, no underscores:
46 * public (extern) routine, documented.
47 * Lower case lettering, with underscores:
48 * private (static) routine, undocumented.
49 *
50 * Cleanup convention:
51 * out_object is to be considered deletable at all times.
52 */
53
54
55
56 /*-----------------------------------*\
57 | Repackage of the input for AddTask
58 \*-----------------------------------*/
59 typedef struct
60 {
61 /* User supplied items */
62 Field (*process_part) ( Field, char *, int );
63 Object self;
64 Pointer args;
65 int size;
66
67 /* internal states */
68 int parallel;
69 int copy;
70 int preserve;
71
72 Object parent;
73 char *member_name;
74 int group_member_position;
75 float series_FP_value;
76 }
77 part_arg_type;
78
79 static part_arg_type part_arg_initializer
80 = { NULL, NULL, NULL, 0, 1, 1, 0, NULL, NULL, 0, 0.0 };
81
82
83 static
84 /*
85 * Set a child for an existing parent as found in arg->parent.
86 * Specifically, arg->parent will refer to child, using the
87 * referencing mechanism appropriate to that object.
88 * Note that on first call with a given parent/child combination
89 * that the child is is not so much replaced as placed.
90 *
91 * The caller is expected to set 'child' to null following this call,
92 * for the purposes of not deleting it twice during error cleanup.
93 */
_replace_child(part_arg_type * arg,Object child)94 Error _replace_child ( part_arg_type *arg, Object child )
95 {
96 DXASSERTGOTO ( arg != NULL );
97 DXASSERTGOTO ( child != NULL );
98 DXASSERTGOTO ( arg->copy == 1 );
99 DXASSERTGOTO ( arg->parent != NULL );
100
101 /* Trace */
102 if ( DXQueryDebug ( PartsDebugChar ) ) {
103 if ( ( DXGetObjectClass ( child ) == CLASS_FIELD )
104 &&
105 DXEmptyField ( (Field) child ) )
106 DXDebug ( PartsDebugChar, "_replace_child with DXEmptyField" );
107 else
108 DXDebug ( PartsDebugChar, "_replace_child" );
109 }
110
111 switch ( DXGetObjectClass ( arg->parent ) )
112 {
113 case CLASS_GROUP:
114 switch ( DXGetGroupClass ( (Group)arg->parent ) )
115 {
116 case CLASS_SERIES:
117 if ( !DXSetSeriesMember
118 ( (Series)arg->parent,
119 arg->group_member_position,
120 arg->series_FP_value,
121 child ) )
122 goto error;
123 break;
124
125 default:
126 /* member name association can preserve order */
127 if ( arg->preserve )
128 {
129 if ( arg->member_name == NULL )
130 {
131 if ( !DXSetEnumeratedMember
132 ( (Group)arg->parent,
133 arg->group_member_position,
134 child ) )
135 goto error;
136 }
137 else
138 {
139 if ( !DXSetMember
140 ( (Group)arg->parent,
141 arg->member_name,
142 child ) )
143 goto error;
144 }
145 }
146 else
147 if ( !DXSetMember
148 ( (Group)arg->parent,
149 arg->member_name,
150 child ) )
151 goto error;
152 }
153 break;
154
155 case CLASS_XFORM:
156 if ( !DXSetXformObject ( (Xform)arg->parent, child ) )
157 goto error;
158 break;
159
160 case CLASS_SCREEN:
161 if ( !DXSetScreenObject ( (Screen)arg->parent, child ) )
162 goto error;
163 break;
164
165 case CLASS_CLIPPED:
166 if ( !DXSetClippedObjects ( (Clipped)arg->parent, child, NULL ) )
167 goto error;
168 break;
169
170 default:
171 DXErrorGoto
172 ( ERROR_ASSERTION,
173 "#11530" /* ProcessParts: Impossible parent */ );
174 }
175
176 return OK;
177
178 error:
179 ASSERT ( DXGetError() != ERROR_NONE );
180
181 return ERROR;
182 }
183
184
185 /*-----------------------------------------*\
186 | The Leaf Processor called by AddTask
187 \*-----------------------------------------*/
188 static
189 /*
190 * Call the process_part 'worker'.
191 * Do so in a way acceptable to DXAddTask.
192 * Check the return states.
193 */
_call_process_part(Pointer p)194 Error _call_process_part ( Pointer p )
195 {
196 part_arg_type *arg = (part_arg_type *) p;
197 Object out_object = NULL;
198
199 DXASSERTGOTO ( arg != NULL );
200
201 out_object = (Object)
202 arg->process_part
203 ( (Field)arg->self, (char *)arg->args, arg->size );
204
205 /*
206 * Check error states and trivial return cases.
207 */
208 if ( out_object == NULL )
209 {
210 if ( DXGetError() != ERROR_NONE )
211 goto error;
212
213 else
214 {
215 /*
216 * If preserve case or series groups, DXEmptyField's have already
217 * been put in as placeholders.
218 * Else, no need to use result. Either way OK
219 */
220 arg->self = NULL;
221
222 /*
223 * But...
224 * Ensure that DXEmptyField is returned if a single-level hierarchy.
225 * (if parent is not NULL then a placeholder is there if need be)
226 */
227 if ( ( arg->copy ) && ( arg->parent == NULL ) )
228 {
229 if ( NULL == ( arg->self = (Object) DXNewField() ) )
230 goto error;
231 }
232
233 return OK;
234 }
235 }
236 else
237 if ( DXGetError() != ERROR_NONE )
238 MessageGotoPlus1
239 ( "#8000",
240 /* %s set the error code, but did not return error */
241 "'process_part' worker" )
242
243 /*
244 * Let's see if a valid object.
245 * And if so, use it.
246 */
247 if ( arg->copy )
248 {
249 if ( arg->self == out_object )
250 DXErrorGoto
251 ( ERROR_ASSERTION, "#11500"
252 /* ProcessParts: worker output can't equal input when called with copy */
253 );
254
255 if ( arg->parent == NULL )
256 arg->self = out_object;
257 else
258 {
259 if ( !_replace_child ( arg, out_object ) )
260 goto error;
261
262 arg->self = out_object;
263 out_object = NULL;
264 }
265 }
266 else
267 if ( arg->self != out_object )
268 DXErrorGoto
269 ( ERROR_ASSERTION, "#11510"
270 /* ProcessParts: worker output can't equal input when called with nocopy */
271 );
272
273 return OK;
274
275 error:
276 if ( ( out_object != NULL ) && ( out_object != arg->self ) )
277 DXDelete ( out_object );
278
279 ASSERT ( DXGetError() != ERROR_NONE );
280
281 return ERROR;
282 }
283
284
285 /*--------------------------------*\
286 | The Group Processor [Recursive]
287 \*--------------------------------*/
288 static
_recurse(part_arg_type * arg,Field placeholder)289 Error _recurse ( part_arg_type *arg, Field placeholder )
290 {
291 part_arg_type call_arg;
292 Object out_object = NULL;
293 Class class;
294
295 /*
296 * Determine if a Field or Non-Field, and act accordingly.
297 */
298 if ( ( class = DXGetObjectClass ( arg->self ) ) == CLASS_FIELD )
299 {
300 if ( arg->parallel == 0 )
301 {
302 if ( !_call_process_part ( (Pointer)arg ) ) goto error;
303
304 out_object = arg->self;
305 }
306 else
307 {
308 /*
309 * (insure that there is a place to put result during parallelism)
310 */
311 DXASSERTGOTO ( arg->parent != NULL );
312
313 /*
314 * Recall that DXAddTask will copy its input when
315 * it is passed in as non-zero length.
316 */
317 if ( !DXAddTask ( _call_process_part,
318 (Pointer)arg,
319 sizeof( part_arg_type ),
320 (double)1.0 ) )
321 goto error;
322
323 /*
324 * Install a place-holder if called for; And that is:
325 * copy and preserve mode
326 * or copy mode with a series group parent
327 * The actual output will be placed after ExectueTaskGroup()
328 * is called.
329 */
330 if ( arg->copy )
331 if ( arg->preserve
332 ||
333 ( ( DXGetObjectClass ( arg->parent )
334 == CLASS_GROUP )
335 &&
336 ( DXGetGroupClass ( (Group)arg->parent )
337 == CLASS_SERIES ) ) )
338 if ( !_replace_child ( arg, (Object)placeholder ) )
339 goto error;
340 }
341 }
342 else /* class != CLASS_FIELD */
343 {
344 call_arg = part_arg_initializer;
345 call_arg.size = arg->size;
346 call_arg.args = arg->args;
347 call_arg.process_part = arg->process_part;
348 call_arg.parallel = arg->parallel;
349 call_arg.copy = arg->copy;
350 call_arg.preserve = arg->preserve;
351
352 if ( arg->copy == 0 )
353 call_arg.parent = arg->self;
354 else
355 {
356 /*
357 * XXX
358 * It is not entirely known how this DXCopy will behave across
359 * the different object types.
360 */
361 if ( !( out_object = DXCopy ( arg->self, COPY_ATTRIBUTES ) ) )
362 goto error;
363
364 call_arg.parent = out_object;
365 }
366
367 switch ( class )
368 {
369 case CLASS_GROUP:
370
371 switch ( class = DXGetGroupClass ( (Group) arg->self ) )
372 {
373 case CLASS_SERIES:
374 call_arg.member_name = NULL;
375 for(call_arg.group_member_position = 0;
376 (call_arg.self=DXGetSeriesMember
377 ( (Series)arg->self,
378 call_arg.group_member_position,
379 &call_arg.series_FP_value ));
380 call_arg.group_member_position++ )
381 if ( !_recurse ( &call_arg, placeholder ) )
382 goto error;
383
384 break;
385
386 default:
387 call_arg.series_FP_value = 0.0;
388 for(call_arg.group_member_position = 0;
389 (call_arg.self=DXGetEnumeratedMember
390 ( (Group)arg->self,
391 call_arg.group_member_position,
392 &call_arg.member_name ));
393 call_arg.group_member_position++ )
394 if ( ! _recurse ( &call_arg, placeholder ) )
395 goto error;
396
397 /* XXX above */
398 }
399 break;
400
401 case CLASS_XFORM:
402 if ( !DXGetXformInfo ( (Xform)arg->self, &call_arg.self, NULL )
403 ||
404 !_recurse ( &call_arg, placeholder ) )
405 goto error;
406 break;
407
408 case CLASS_SCREEN:
409 if ( !DXGetScreenInfo
410 ( (Screen)arg->self, &call_arg.self, NULL,NULL )
411 ||
412 !_recurse ( &call_arg, placeholder ) )
413 goto error;
414 break;
415
416 case CLASS_CLIPPED:
417 if ( !DXGetClippedInfo
418 ( (Clipped)arg->self, &call_arg.self, NULL )
419 ||
420 !_recurse ( &call_arg, placeholder ) )
421 goto error;
422 break;
423
424 default:
425 DXASSERTGOTO ( class != CLASS_FIELD );
426 DXDebug ( PartsDebugChar, "funny class: %d", class );
427 }
428
429 /*
430 * Place the computed output in the new hierarchy;
431 */
432 if ( arg->copy )
433 {
434 if ( arg->parent == NULL )
435 arg->self = out_object;
436 else
437 {
438 if ( !_replace_child ( arg, out_object ) )
439 goto error;
440
441 arg->self = out_object;
442 out_object = NULL;
443 }
444 }
445 }
446
447 return OK;
448
449 error:
450 if ( ( out_object != NULL ) && ( out_object != arg->self ) )
451 DXDelete ( out_object );
452
453 ASSERT ( DXGetError() != ERROR_NONE );
454
455 return ERROR;
456 }
457
458
459 static int _protected_parallel_switch = 1;
460
461 extern
_dxfProcessPartsNP(Object object,Field (* process_part)(Field,char *,int),Pointer args,int size,int copy,int preserve)462 Object _dxfProcessPartsNP ( Object object,
463 Field (*process_part) ( Field, char *, int ),
464 Pointer args,
465 int size,
466 int copy,
467 int preserve )
468 {
469 Object out_object;
470
471 _protected_parallel_switch = 0;
472
473 out_object = DXProcessParts
474 ( object, process_part, args, size, copy, preserve );
475
476 _protected_parallel_switch = 1;
477
478 return out_object;
479
480 }
481
482
483 extern
DXProcessParts(Object object,Field (* process_part)(Field,char *,int),Pointer args,int size,int copy,int preserve)484 Object DXProcessParts ( Object object,
485 Field (*process_part) ( Field, char *, int ),
486 Pointer args,
487 int size,
488 int copy,
489 int preserve )
490 {
491 /** prototype new argument **/
492 int parallel = _protected_parallel_switch;
493 /**/
494
495 part_arg_type call_arg;
496 Pointer args_copy_global = NULL;
497 Field placeholder = NULL;
498
499 call_arg = part_arg_initializer;
500
501 if (object == NULL) {
502 DXSetError (ERROR_MISSING_DATA, "#10000", "Object");
503 return NULL;
504 }
505
506 call_arg.self = object;
507 call_arg.args = args;
508 call_arg.size = size;
509 call_arg.process_part = process_part;
510 call_arg.parallel = parallel;
511 call_arg.copy = copy;
512 call_arg.preserve = preserve;
513
514 /*
515 * Note: the assumption currently is made that sizeof(args) = size
516 */
517 if (size > 0 && args != NULL)
518 {
519 /*
520 * Assume worst case that the argument 'args' is a pointer to
521 * local memory. For this reason, 'args' is copied to global
522 * memory to ensure validity across all processors accessed by
523 * DXAddTask.
524 */
525
526 if ((args_copy_global = DXAllocate (size)) == ERROR)
527 goto error;
528
529 memcpy (args_copy_global, args, size);
530 call_arg.args = args_copy_global;
531 }
532
533
534 /*
535 * When not copying, then order is preserved as a byproduct of
536 * the modify in place operation. Preserve setting can be ignored.
537 */
538
539 switch (DXGetObjectClass (call_arg.self))
540 {
541 case CLASS_FIELD:
542 call_arg.parallel = 0;
543
544 case CLASS_GROUP:
545 case CLASS_XFORM:
546 case CLASS_SCREEN:
547 case CLASS_CLIPPED:
548 if ((call_arg.copy == 1) && (call_arg.parallel == 1))
549 /*
550 * placeholder will be needed in cases of explicit preserve
551 * order, or for series groups, which imply this.
552 */
553 if (((placeholder = DXNewField()) == NULL)
554 ||
555 !DXReference ((Object)placeholder))
556 goto error;
557
558 if (call_arg.parallel == 1)
559 {
560 if (!DXCreateTaskGroup() ||
561 ! _recurse (&call_arg, placeholder) ||
562 !DXExecuteTaskGroup())
563 goto error;
564 }
565 else
566 if (! _recurse (&call_arg, placeholder))
567 goto error;
568
569 DXDelete ((Object)placeholder);
570 break;
571
572 default:
573 DXSetError (ERROR_BAD_CLASS, "#10190", "Object");
574 goto error;
575 }
576
577 if (args_copy_global && !DXFree (args_copy_global))
578 goto error;
579 else
580 args_copy_global = NULL;
581
582 return (call_arg.self);
583
584 error:
585 if ( copy && ( call_arg.self != object ) )
586 DXDelete ( call_arg.self );
587
588 if ( args_copy_global )
589 DXFree ( args_copy_global );
590
591 DXDelete ( (Object)placeholder );
592
593 return ERROR;
594 }
595
596
597
598 extern
_dxfProcessPartsG(Object object,Group (* process_part)(Group,char *,int),Pointer args,int size,int copy,int preserve)599 Object _dxfProcessPartsG ( Object object,
600 Group (*process_part) ( Group, char *, int ),
601 Pointer args,
602 int size,
603 int copy,
604 int preserve )
605 {
606 Object child = NULL;
607 Group result = NULL;
608 int i;
609
610 /* Parallel !!! */
611
612 #if 0
613 "#11500"
614 /* ProcessParts: worker output cannot equal input when called with copy */
615 "#11520"
616 /* ProcessParts: args is NULL but size is nonzero */
617 #endif
618
619 if ( ( copy != 0 ) ||
620 ( preserve != 1 ) )
621 DXErrorGoto
622 ( ERROR_NOT_IMPLEMENTED,
623 "DXProcessPartsG must be called with copy = 0, preserve = 1" );
624
625 switch ( DXGetObjectClass ( object ) )
626 {
627 case CLASS_FIELD:
628 case CLASS_ARRAY:
629 default:
630 #if 0
631 DXErrorGoto ( ERROR_BAD_CLASS, "encountered in object traversal" );
632 #endif
633 break;
634
635 case CLASS_GROUP:
636 switch ( DXGetGroupClass ( (Group) object ) )
637 {
638 case CLASS_GROUP:
639 default:
640 for (i = 0;
641 ( NULL != ( child = DXGetEnumeratedMember
642 ( (Group)object, i, NULL ) ) );
643 i++ )
644 if ( !_dxfProcessPartsG
645 ( child, process_part,
646 args, size, copy, preserve ) )
647 goto error;
648 #if 0
649 DXSetEnumeratedMember((Group)o, i, n);
650 #endif
651
652 case CLASS_COMPOSITEFIELD:
653 case CLASS_SERIES:
654 if ( ERROR == ( result = process_part
655 ( (Group)object,
656 (char*)args, size ) ) )
657 goto error;
658
659 if ( ( copy == 0 )
660 &&
661 ( result != (Group)object ) )
662 DXErrorGoto
663 ( ERROR_ASSERTION, "#11510"
664 /* ProcessParts: worker output must equal
665 input when called with nocopy */ );
666 }
667 break;
668
669 case CLASS_XFORM:
670 if ( !DXGetXformInfo ( (Xform)object, &child, NULL ) )
671 goto error;
672
673 if ( !_dxfProcessPartsG
674 ( child, process_part, args, size, copy, preserve ) )
675 goto error;
676 break;
677
678 #if 0
679 DXSetXformObject((Xform)o,n);
680 #endif
681
682 case CLASS_SCREEN:
683 if ( !DXGetScreenInfo((Screen)object, &child, NULL, NULL ) )
684 goto error;
685
686 if ( !_dxfProcessPartsG
687 ( child, process_part, args, size, copy, preserve ) )
688 goto error;
689 break;
690
691 #if 0
692 DXSetScreenObject((Screen)o,n);
693 #endif
694
695 case CLASS_CLIPPED:
696 if ( !DXGetClippedInfo((Clipped)object, &child, NULL ) )
697 goto error;
698
699 if ( !_dxfProcessPartsG
700 ( child, process_part, args, size, copy, preserve ) )
701 goto error;
702 break;
703
704 #if 0
705 DXSetClippedObjects((Clipped)o,n,NULL);
706 #endif
707 }
708
709 return object;
710
711 error:
712 return ERROR;
713
714 }
715