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 (¨a->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 (¨a->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 (¨a->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 (¨a->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