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