1 /***************************************************************************
2                           generate_code_ada.c  -  description
3                              -------------------
4     begin                : 2001-07-18
5     copyright            : (C) 2001 by Thomas Preymesser
6                            (C) 2002 by Oliver Kellogg
7     email                : tp@thopre.de
8                            okellogg@users.sourceforge.net
9  ***************************************************************************/
10 
11 /***************************************************************************
12  *                                                                         *
13  *   This program is free software; you can redistribute it and/or modify  *
14  *   it under the terms of the GNU General Public License as published by  *
15  *   the Free Software Foundation; either version 2 of the License, or     *
16  *   (at your option) any later version.                                   *
17  *                                                                         *
18  ***************************************************************************/
19 
20 #include "dia2code.h"
21 #include "decls.h"
22 #include "includes.h"
23 
24 #define PACKAGE_EXT "_types"
25 #define SPEC_EXT "ads"
26 #define BODY_EXT "adb"
27 
28 int use_convention_c = 0;  /* Temporarily here. */
29 int strict_corba = 0;      /* Should be turned into command line options.  */
30 
31 
32 static batch *gb;   /* The current batch being processed.  */
33 
34 /* Utilities.  */
35 
36 static void
check_umlattr(umlattribute * u,char * typename_)37 check_umlattr (umlattribute *u, char *typename_)
38 {
39     /* Check settings that don't make sense for Ada generation.  */
40     if (u->visibility == '1')
41         fprintf (stderr, "%s/%s: ignoring non-visibility\n", typename_, u->name);
42     if (u->isstatic)
43         fprintf (stderr, "%s/%s: ignoring staticness\n", typename_, u->name);
44 }
45 
46 static char *
subst(char * str,char search,char replace)47 subst (char *str, char search, char replace)
48 {
49     char *p;
50     while ((p = strchr (str, search)) != NULL)
51         *p = replace;
52     return str;
53 }
54 
55 static char *
nospc(char * str)56 nospc (char *str)
57 {
58     return subst (str, ' ', '_');
59 }
60 
61 static int
is_oo_class(umlclass * cl)62 is_oo_class (umlclass *cl)
63 {
64     char *st;
65     if (cl == NULL)
66         return 0;
67     st = cl->stereotype;
68     if (strlen (st) == 0)
69         return 1;
70     return (!is_const_stereo (st) &&
71             !is_typedef_stereo (st) &&
72             !is_enum_stereo (st) &&
73             !is_struct_stereo (st) &&
74             !eq (st, "CORBAUnion") &&
75             !eq (st, "CORBAException"));
76 }
77 
78 static int
has_oo_class(declaration * d)79 has_oo_class (declaration *d)
80 {
81     while (d != NULL) {
82         if (d->decl_kind == dk_module) {
83             if (has_oo_class (d->u.this_module->contents))
84                 return 1;
85         } else {         /* dk_class */
86             if (is_oo_class (d->u.this_class->key))
87                 return 1;
88         }
89         d = d->next;
90     }
91     return 0;
92 }
93 
94 static char *
adaname(char * name)95 adaname (char *name)
96 {
97     static char buf[SMALL_BUFFER];
98     if (use_corba &&
99         (eq (name, "boolean") ||
100          eq (name, "char") ||
101          eq (name, "octet") ||
102          eq (name, "short") ||
103          eq (name, "long") ||
104          eq (name, "long long") ||
105          eq (name, "unsigned short") ||
106          eq (name, "unsigned long") ||
107          eq (name, "unsigned long long") ||
108          eq (name, "float") ||
109          eq (name, "double") ||
110          eq (name, "string") ||
111          eq (name, "any"))) {
112         sprintf (buf, "CORBA.%s", nospc (strtoupperfirst (name)));
113     } else {
114         umlclassnode *ref = find_by_name (gb->classlist, name);
115         strcpy (buf, name);
116         if (ref != NULL && is_oo_class (ref->key))
117             strcat (buf, ".Value_Ref");
118     }
119     return buf;
120 }
121 
122 
123 static void
do_operations(char * typename,umloplist umlo,int in_valuetype)124 do_operations (char *typename, umloplist umlo, int in_valuetype)
125 {
126     if (umlo == NULL)
127         return;
128 
129     print ("-- Operations\n\n");
130 
131     while (umlo != NULL) {
132         int use_procedure = (strlen (umlo->key.attr.type) == 0 ||
133                                  eq (umlo->key.attr.type, "void"));
134         umlattrlist parm = umlo->key.parameters;
135 
136         print ("");
137         if (use_procedure)
138             emit ("procedure");
139         else
140             emit ("function ");
141         emit (" %s (", umlo->key.attr.name);
142         if (! umlo->key.attr.isstatic) {
143             emit ("Self : access Object");
144             if (parm != NULL)
145                 emit (";\n");
146         } else {
147             emit ("\n");
148         }
149         indentlevel += 5;
150 
151         while (parm != NULL) {
152             /* FIXME: Add support for parameter modes in dia.  */
153             print ("%s : in %s", parm->key.name, adaname (parm->key.type));
154             if (parm->key.value[0] != 0)
155                 emit (" := %s", parm->key.value);
156             parm = parm->next;
157             if (parm != NULL) {
158                 emit (";\n");
159             }
160         }
161 
162         emit (")");
163 
164         if (! use_procedure)
165             emit (" return %s", adaname (umlo->key.attr.type));
166 
167         if (umlo->key.attr.isabstract || in_valuetype)
168             emit (" is abstract");
169         /* TBH, we have no way of generating a meaningful implementation.
170            Instead, the user shall derive from this class and implement the
171            UML defined methods in the derived class.  */
172 
173         emit (";\n\n");
174         indentlevel -= 5;
175         umlo = umlo->next;
176     }
177 }
178 
179 
180 static char *
fqname(umlclassnode * node,int use_ref_type)181 fqname (umlclassnode *node, int use_ref_type)
182 {
183     static char buf[BIG_BUFFER];
184 
185     buf[0] = '\0';
186     if (node == NULL)
187         return buf;
188     if (node->key->package != NULL) {
189         umlpackagelist pkglist = make_package_list (node->key->package);
190         while (pkglist != NULL) {
191             strcat (buf, pkglist->key->name);
192             strcat (buf, ".");
193             pkglist = pkglist->next;
194         }
195     }
196     strcat (buf, node->key->name);
197     if (is_oo_class (node->key)) {
198         if (use_ref_type)
199             strcat (buf, ".Value_Ref");
200         else
201             strcat (buf, ".Object");
202     }
203     return buf;
204 }
205 
206 static int
gen_static_attributes(umlattrlist umla,int do_private)207 gen_static_attributes (umlattrlist umla, int do_private)
208 {
209     int number_of_static_attributes = 0;
210     int did_output = 0;
211 
212     while (umla != NULL) {
213         char *member = umla->key.name;
214         umlclassnode *ref;
215 
216         if (!umla->key.isstatic ||
217             (umla->key.visibility != '0' && !do_private)) {
218             umla = umla->next;
219             continue;
220         }
221         number_of_static_attributes++;
222         if (! did_output) {
223             print ("-- Static attributes\n\n");
224             did_output = 1;
225         }
226         ref = find_by_name (gb->classlist, umla->key.type);
227         if (umla->key.visibility == '0' && !do_private) {
228             pboth ("procedure Set_%s (To : ", member);
229             if (ref != NULL)
230                 eboth ("%s", fqname (ref, 1));
231             else
232                 eboth ("%s", adaname (umla->key.type));
233             eboth (")");
234             emit (";\n");
235             ebody (" is\n");
236             pbody ("begin\n");
237             pbody ("   %s := To;\n", member);
238             pbody ("end Set_%s;\n\n", member);
239             pboth ("function Get_%s return ", member);
240             if (ref != NULL)
241                 eboth ("%s", fqname (ref, 1));
242             else
243                 eboth ("%s", adaname (umla->key.type));
244             emit (";\n\n");
245             ebody (" is\n");
246             pbody ("begin\n");
247             pbody ("   return %s;\n", member);
248             pbody ("end Get_%s;\n\n", member);
249         }
250         if (do_private) {
251             print ("%s : ", member);
252             if (ref != NULL)
253                 emit ("%s", fqname (ref, 0));
254             else
255                 emit ("%s", adaname (umla->key.type));
256             emit (";\n\n");
257         }
258         umla = umla->next;
259     }
260     return number_of_static_attributes;
261 }
262 
263 static void
gen_class(umlclassnode * node,int do_valuetype)264 gen_class (umlclassnode *node, int do_valuetype)
265 {
266     char *name = node->key->name;
267     char parentname[SMALL_BUFFER];
268     int n_static_attrs;
269 
270     pboth ("package ");
271     ebody ("body ");
272     eboth ("%s is\n\n", name);
273     indentlevel++;
274     n_static_attrs = gen_static_attributes (node->key->attributes, 0);
275     if (do_valuetype && n_static_attrs > 0) {
276         fprintf (stderr, "Static attributes not permitted for %s\n", name);
277     }
278     print ("type Object is ");
279     if (node->key->isabstract)
280         emit ("abstract ");
281     parentname[0] = '\0';
282     if (node->parents != NULL) {
283         umlclassnode *parent = node->parents;
284         sprintf (parentname, "%s.Object", parent->key->name);
285         if (parent->next != NULL)
286             fprintf (stderr, "Warning: multiple inheritance NYI (%s)\n", name);
287     } else if (do_valuetype) {
288         sprintf (parentname, "CORBA.Value.Base");
289     }
290     if (parentname[0])
291         emit ("new %s with", parentname);
292     else
293         emit ("tagged");
294     emit (" private;\n\n");
295     print ("type Value_Ref is access all Object'Class;\n\n");
296     if (node->key->attributes) {
297         umlattrlist umla = node->key->attributes;
298         int did_output = 0;
299         while (umla != NULL) {
300             char *member = umla->key.name;
301             umlclassnode *ref;
302 
303             if (umla->key.visibility != '0' || umla->key.isstatic) {
304                 umla = umla->next;
305                 continue;
306             }
307             if (! did_output) {
308                 print ("-- Public attributes\n\n");
309                 did_output = 1;
310             }
311             pboth ("procedure Set_%s (Self : access Object; To : ", member);
312             ref = find_by_name (gb->classlist, umla->key.type);
313             if (ref != NULL)
314                 eboth ("%s", fqname (ref, 1));
315             else
316                 eboth ("%s", adaname (umla->key.type));
317             eboth (")");
318             emit (";\n");
319             ebody (" is\n");
320             pbody ("begin\n");
321             pbody ("   Self.%s := To;\n", member);
322             pbody ("end Set_%s;\n\n", member);
323             pboth ("function Get_%s (Self : access Object) return ", member);
324             if (ref != NULL)
325                 eboth ("%s", fqname (ref, 1));
326             else
327                 eboth ("%s", adaname (umla->key.type));
328             emit (";\n\n");
329             ebody (" is\n");
330             pbody ("begin\n");
331             pbody ("   return Self.%s;\n", member);
332             pbody ("end Get_%s;\n\n", member);
333             umla = umla->next;
334         }
335     }
336     do_operations (name, node->key->operations, do_valuetype);
337     indentlevel--;
338     print ("private\n\n");
339     indentlevel++;
340     n_static_attrs = gen_static_attributes (node->key->attributes, 1);
341     if (do_valuetype && n_static_attrs > 0) {
342         fprintf (stderr, "Static attributes not permitted for %s\n", name);
343     }
344     print ("type Object is ");
345     if (node->key->isabstract)
346         emit ("abstract ");
347     if (parentname[0])
348         emit ("new %s with", parentname);
349     else
350         emit ("tagged");
351     emit (" record\n");
352     indentlevel++;
353     if (node->key->attributes) {
354         umlattrlist umla = node->key->attributes;
355         print ("-- Attributes\n");
356         while (umla != NULL) {
357             umlclassnode *ref;
358             if (umla->key.isstatic) {
359                 umla = umla->next;
360                 continue;
361             }
362             print ("%s : ", umla->key.name);
363             ref = find_by_name (gb->classlist, umla->key.type);
364             if (ref != NULL)
365                 emit ("%s", fqname (ref, 1));
366             else
367                 emit ("%s", adaname (umla->key.type));
368             emit (";\n");
369             umla = umla->next;
370         }
371     } else if (node->associations == NULL) {
372         print ("null;\n");
373     }
374     if (node->associations) {
375         umlassoclist assoc = node->associations;
376         print ("-- Associations\n");
377         while (assoc != NULL) {
378             umlclassnode *ref;
379             ref = find_by_name (gb->classlist, assoc->key->name);
380             print ("%s : ", assoc->name);
381             if (ref != NULL) {
382                 if (is_oo_class (ref->key) && do_valuetype && assoc->composite)
383                     fprintf (stderr, "Association %s cannot be composite\n",
384                                      assoc->key->name);
385                 emit ("%s", fqname (ref, !assoc->composite));
386             } else {
387                 emit ("%s", adaname (assoc->key->name));
388             }
389             emit (";\n");
390             assoc = assoc->next;
391         }
392     }
393     indentlevel--;
394     print ("end record;\n\n");
395     indentlevel--;
396     pboth ("end %s;\n\n", name);
397 }
398 
399 
400 static void
convention_c(char * name)401 convention_c (char *name)
402 {
403     if (use_convention_c)
404         print ("pragma Convention (C, %s);\n", name);
405 }
406 
407 static void
gen_decl(declaration * d)408 gen_decl (declaration *d)
409 {
410     char *name;
411     char *stype;
412     umlclassnode *node;
413     umlattrlist umla;
414 
415     if (d == NULL)
416         return;
417 
418     if (d->decl_kind == dk_module) {
419         name = d->u.this_module->pkg->name;
420         pboth ("package ");
421         ebody ("body ");
422         eboth ("%s is\n\n", name);
423         indentlevel++;
424         d = d->u.this_module->contents;
425         while (d != NULL) {
426             gen_decl (d);
427             d = d->next;
428         }
429         indentlevel--;
430         pboth ("end %s;\n\n", name);
431         return;
432     }
433 
434     node = d->u.this_class;
435     stype = node->key->stereotype;
436     name = node->key->name;
437     umla = node->key->attributes;
438 
439     if (strlen (stype) == 0) {
440         gen_class (node, 0);
441         return;
442     }
443 
444     if (eq (stype, "CORBANative")) {
445         print ("-- CORBANative: %s\n\n", name);
446 
447     } else if (is_const_stereo (stype)) {
448         if (umla == NULL) {
449             fprintf (stderr, "Error: first attribute not set at %s\n", name);
450             exit (1);
451         }
452         if (strlen (umla->key.name) > 0)
453             fprintf (stderr, "Warning: ignoring attribute name at %s\n", name);
454 
455         print ("%s : constant %s := %s;\n\n", name, adaname (umla->key.type),
456                                                              umla->key.value);
457 
458     } else if (is_enum_stereo (stype)) {
459         print ("type %s is (\n", name);
460         indentlevel++;
461         while (umla != NULL) {
462             char *literal = umla->key.name;
463             check_umlattr (&umla->key, name);
464             if (strlen (umla->key.type) > 0)
465                 fprintf (stderr, "%s/%s: ignoring type\n", name, literal);
466             print ("%s", literal);
467             if (umla->next)
468                 emit (",\n");
469             umla = umla->next;
470         }
471         emit (");\n");
472         indentlevel--;
473         convention_c (name);
474         emit ("\n");
475 
476     } else if (is_struct_stereo (stype)) {
477         print ("type %s is record\n", name);
478         indentlevel++;
479         while (umla != NULL) {
480             check_umlattr (&umla->key, name);
481             print ("%s : %s", umla->key.name, adaname (umla->key.type));
482             if (strlen (umla->key.value) > 0)
483                 emit (" := %s", umla->key.value);
484             emit (";\n");
485             umla = umla->next;
486         }
487         indentlevel--;
488         print ("end record;\n");
489         convention_c (name);
490         emit ("\n");
491 
492     } else if (eq (stype, "CORBAException")) {
493         print ("%s : exception;\n\n", name);
494         if (strict_corba) {
495             print ("type %s_Members is new CORBA.IDL_Exception_Members"
496                    " with record\n", name);
497             indentlevel++;
498             while (umla != NULL) {
499                 check_umlattr (&umla->key, name);
500                 print ("%s : %s;\n", umla->key.name, adaname (umla->key.type));
501                 umla = umla->next;
502             }
503             indentlevel--;
504             print ("end record;\n\n");
505         }
506 
507     } else if (eq (stype, "CORBAUnion")) {
508         umlattrnode *sw = umla;
509         char swname[SMALL_BUFFER];
510         if (sw == NULL) {
511             fprintf (stderr, "Error: attributes not set at union %s\n", name);
512             exit (1);
513         }
514         if (strlen (sw->key.name) == 0)
515             sprintf (swname, "Switch");
516         else
517             sprintf (swname, "%s", sw->key.name);
518         print ("type %s (%s : %s := %s'First) is record\n",
519                name, swname, sw->key.type, sw->key.type);
520         indentlevel++;
521         print ("case %s is\n", swname);
522         indentlevel++;
523         umla = umla->next;
524         while (umla != NULL) {
525             check_umlattr (&umla->key, name);
526             print ("when %s =>\n", umla->key.value);
527             print ("   %s : %s;\n", umla->key.name, adaname (umla->key.type));
528             umla = umla->next;
529         }
530         indentlevel--;
531         print ("end case;\n");
532         indentlevel--;
533         print ("end record;\n");
534         convention_c (name);
535         emit ("\n");
536 
537     } else if (is_typedef_stereo (stype)) {
538         char dim[SMALL_BUFFER];
539 
540         /* Conventions for CORBATypedef:
541            The first (and only) attribute contains the following:
542            Name:   Empty - the name is taken from the class.
543            Type:   Name of the original type which is typedefed.
544            Value:  Optionally contains array dimension(s) of the typedef.
545                    These dimensions are given in square brackets, e.g.
546                    [3][10]
547          */
548         if (umla == NULL) {
549             fprintf (stderr, "Error: first attribute (impl type) not set "
550                              "at typedef %s\n", name);
551             exit (1);
552         }
553         if (strlen (umla->key.name) > 0)  {
554             fprintf (stderr, "Warning: typedef %s: ignoring name field "
555                         "in implementation type attribute\n", name);
556         }
557         if (*umla->key.value) {
558             strcpy (dim, umla->key.value);
559             subst (dim, '[', '(');
560             subst (dim, ']', ')');
561         } else {
562             dim[0] = '\0';
563         }
564         print ("");
565         if (!*dim && !strict_corba)
566             emit ("sub");
567         emit ("type %s is ", name);
568         if (*dim) {
569             emit ("array (");
570             if (strict_corba)
571                 emit ("0 .. ");
572             else
573                 emit ("1 .. ");
574             emit ("%s", dim);  /* multi-dimension support is TBD */
575             emit (" of ");
576         } else if (strict_corba) {
577             emit ("new ");
578         }
579         emit ("%s;\n\n", adaname (umla->key.type));
580 
581     } else if (eq (stype, "CORBAValue")) {
582         gen_class (node, 1);
583 
584     } else {
585         print ("--  %s\n", stype);
586         gen_class (node, 0);
587     }
588 }
589 
590 static char *
make_filename(char * name,int do_body)591 make_filename (char *name, int do_body)
592 {
593     static char outfname[BIG_BUFFER];
594     char *filebase = strtolower (name);
595 
596     subst (filebase, '.', '-');
597     if (do_body)
598         sprintf (outfname, "%s.%s", filebase, body_file_ext);
599     else
600         sprintf (outfname, "%s.%s", filebase, file_ext);
601     return outfname;
602 }
603 
604 
605 void
generate_code_ada(batch * b)606 generate_code_ada (batch *b)
607 {
608     declaration *d;
609     umlclasslist tmplist = b->classlist;
610     FILE *licensefile = NULL;
611 
612     gb = b;
613 
614     if (file_ext == NULL)
615         file_ext = SPEC_EXT;
616     if (body_file_ext == NULL)
617         body_file_ext = BODY_EXT;
618 
619     /* open license file */
620     if (b->license != NULL) {
621         licensefile = fopen (b->license, "r");
622         if (!licensefile) {
623             fprintf (stderr, "Can't open the license file.\n");
624             exit (1);
625         }
626     }
627 
628     while (tmplist != NULL) {
629         if (! (is_present (b->classes, tmplist->key->name) ^ b->mask)) {
630             push (tmplist, b);
631         }
632         tmplist = tmplist->next;
633     }
634 
635     set_number_of_spaces_for_one_indentation (3);
636 
637     /* Generate a file for each outer declaration.  */
638     d = decls;
639     while (d != NULL) {
640         char *name, basename[BIG_BUFFER];
641         int synthesize_package = 0;
642         int need_body = 0;
643 
644         if (d->decl_kind == dk_module) {
645             name = d->u.this_module->pkg->name;
646             strcpy (basename, name);
647             need_body = has_oo_class (d->u.this_module->contents);
648         } else {         /* dk_class */
649             name = d->u.this_class->key->name;
650             strcpy (basename, name);
651             if (is_oo_class (d->u.this_class->key)) {
652                 need_body = 1;
653             } else {
654                 strcat (basename, PACKAGE_EXT);
655                 synthesize_package = 1;
656             }
657         }
658 
659         spec = open_outfile (make_filename (basename, 0), b);
660         if (spec == NULL) {
661             d = d->next;
662             continue;
663         }
664 
665         /* add license to the header */
666         if (b->license != NULL) {
667             int lc;
668             rewind (licensefile);
669             while ((lc = fgetc (licensefile)) != EOF)
670                 print ("%c", (char) lc);
671         }
672 
673         includes = NULL;
674         determine_includes (d, b);
675         if (use_corba)
676             print ("with CORBA.Value;\n\n");
677         if (includes) {
678             namelist incfile = includes;
679             while (incfile != NULL) {
680                 if (!eq (incfile->name, name)) {
681                     print ("with %s;\n", incfile->name);
682                 }
683                 incfile = incfile->next;
684             }
685             print ("\n");
686         }
687 
688         if (synthesize_package) {
689             emit ("package %s is\n\n", basename);
690             indentlevel++;
691         } else if (need_body) {
692             body = open_outfile (make_filename (basename, 1), b);
693         } else {
694             body = NULL;
695         }
696 
697         gen_decl (d);
698 
699         if (synthesize_package) {
700             indentlevel--;
701             emit ("end %s;\n\n", basename);
702         } else if (body != NULL) {
703             fclose (body);
704             body = NULL;
705         }
706 
707         fclose (spec);
708 
709         d = d->next;
710     }
711 }
712 
713