Ada Code Generation
ContentsThis chapter is organized as follows:
- What is the Ada Generator?
- Basic Steps for Iterative Code Development
- Refining the Subsystem and View Structure
- Specifying Additional Ada Unit Contents
What is the Ada Generator?The Ada Generator is the code generation capability that is provided by the Ada 95/Ada 83 add-in to Rational Rose. The commands for the Ada Generator are located in the Ada 95/Ada 83 submenu of the Rose Tools menu.
You use the Ada Generator to generate Ada units from information in a Rose model. These units contain Ada code constructs that correspond to the notation items (classes, relationships, and adornments) you have defined in the model via diagrams and specifications.
The Ada Generator provides code-generation properties that control the kinds of Ada code constructs that are generated for the various kinds of notation items in the model. You can use the default values for these properties or you can specify different values to generate the code you want.
The Ada Generator inserts specially-marked code regions into the generated files where you can add further code (for example, to fill in extra private declarations in a package specification). By default, such regions are preserved, so you can regenerate the file without losing the code you added.
The Ada Generator may generate code in a directory hierarchy or, if Rational Apex is available, in subsystems and views. In order to generate code in subsystems and views, the Apex add-in must be activated, and the property CreateApexSubsystemAndView of the Apex add-in must be set to "yes". The Ada Generator, when generating code for Apex, makes use of some properties defined by the Apex add-in. These properties have a name which starts with "Apex" and are described in the documentation for the Apex add-in.
Basic Steps for Iterative Code DevelopmentThe basic strategy for generating code is to use the default values for code-generation properties initially, and later introduce non-default values as needed. This section describes these steps for generating Ada units from a Rose model:
- Overview
- The Generated Files
- The Basic Code Contents
- Entering Parameters for Parameterized Classes
- Entering Static Attributes and Metaclass Attributes
- Evaluating the Generated Code
- Completing the Implementation of the Generated Code
- Regenerating Code
Overview
In order to generate Ada 95 or Ada 83 code, you must first activate the Ada 95/Ada 83 add-in using the Add-In Manager, which is accessible from the Add-Ins menu.
Then, you must set the default language for your model to be Ada 95 or Ada 83: choose the Tools > Options menu item, and in the Options dialog box click the Notation tab; use the Default Language list to select Ada 95 or Ada 83.
You may generate a different language for some classes by associating them with a component that has a different language.
By default, code is generated in the current directory or working view (determined initially when you start Rose and changed each time you open a model in a different view). If this is unacceptable, you can specify a default view before generating code.
- 1 Start Rose, if necessary.
- 2 Create or open the Rose model from which you want to generate code and display an appropriate class diagram.
- 3 Select one or more class items (classes, utilities, parameterized classes and bound classes) or UML packages.
- 4 Choose the Code Generation command from the Tools > Ada 95 submenu. If code generation fails, inspect the log.
- 5 Evaluate the generated code. Based on your evaluation, you can change the model and/or code-generation properties, and then regenerate the code.
The Generated Files
The generated files are placed in a directory based on the properties of the model and the component UML packages. By default, each logical or component UML package in Rose is associated with an Apex view within a subsystem (if Apex is available) or with a hierarchy of directories (if Apex is not available).
In general one specification file (.1.ada) is generated for each class you selected in the diagram. The name of each file is derived from the name of the corresponding class. If you selected a UML package, a file is generated for each class in the UML package.
Note that the generated file structure realizes the physical portion of your Rose model. If you have developed only a logical model (class diagrams), the Ada Generator assumes an implicit physical model in which each class is effectively assigned to an implicit module specification, and therefore an Ada package specification.
The Basic Code Contents
The content of the generated code is based on the notation items in the logical portion of your model. In general:
- Each selected class generates a private record declaration and visible operations in a package specification. In addition, an optional access type, known as a handle, can be generated.
- Each of a class's "has" relationships generates a component. The relationship's containment and multiplicity partly determine the type of the component, and may create additional supporting type declarations.
- Each of a class's navigable association roles generates a component. The role's containment and multiplicity partly determine the type of the component, and may create additional supporting type declarations.
- Each operation in a class specification generates a subprogram declaration in the package specification.
- (Ada 95) Generalization relationships generate type derivation.
- (Ada 83) Generalization relationships generate components in the record declaration. In addition, all non-standard operations in the superclass are duplicated in the subclass package specification.
- Each selected utility generates a package specification with subprogram and object declarations only.
- "Has", generalization, association and dependency relationships result in appropriate with clauses.
- If desired, a body is generated for each specification, with stubbed code for the user-defined operations.
The Ada Generator takes into account all model information that pertains to the selected class items, even information that does not appear in the diagram. For example, a component is generated for every "has" relationship that is defined for a class, including "has" relationships defined on other diagrams or in the class specification.
Entering Parameters for Parameterized Classes
The parameters for parameterized classes are entered in Rose using a dialog box which has two fields: Name and Type. Because there is such a large variety of formal parameters in Ada generics, and of discriminants in unconstrained types, users must follow a convention that specifies the nature of the parameters. Roughly speaking, the Name field contains the name of the parameter, and may start with an Ada keyword that indicates its nature. The type field contains any additional information that may be needed to complete the formal parameter or discriminant declaration. The Ada Generator adds the syntactic glue required by the language, such as the reserved words with, is, new, and the colons and semicolons.
Here is a detailed list of the possible formal parameters, and how they may be entered in the Type and Name fields. Note that an anonymous access type is only allowed if the Unconstrained Type implementation is used. Conversely, formal types, procedures, functions, packages, and formal object with an explicit mode are only legal if the Generic implementation is used.
- Generic formal object: the Name field contains the name of the object; the Type field contains its type, possibly followed by a default value, and possibly preceded by a mode. For example:
Name: Foo Type: in out Bar
Name: Foo Type: Bar := 3
In the case of the Unconstrained Type implementation, the above notation may be used to represent an access discriminant:
Name: Foo Type: access Bar
- Generic formal type: the Name field contains the reserved word type, followed by the name of the type, and by discriminants, if any; the Type field contains the type definition. For example:
Name: type Foo (D : Integer := 3) Type: tagged private
- Generic formal procedure: the Name field contains the reserved word procedure, followed by the name and parameters of the procedure; the Type field contains the default name for the formal procedure, if any. For example:
Name: procedure Foo (X : in out Integer) Type: Bar.Func
- Generic formal function: the Name field contains the reserved word function, followed by the name, parameters and result type of the function; the Type field contains the default name for the formal function, if any. For example:
Name: function Foo (X : Float) return Boolean Type: <>
- Generic formal package (Ada 95): the Name field contains the reserved word package, followed by the name of the formal package. The Type field contains the name of the corresponding generic package, followed by instantiation parameters. For example:
Name: package Foo Type: List_Generic (<>)
For actual parameters (appearing in bound classes) the convention is the following: the Name field contains the value of the actual parameter, and the Type field contains the name of the formal parameter. For example, if a parameterized class has the following parameters:
Name: Foo Type: Floatit may be instantiated using the following parameters:
Name: 3.14 Type: FooEntering Static Attributes and Metaclass Attributes
Static attributes and metaclass attributes can result in a wide variety of (package-level) declarations. They are entered in Rose using a dialog box which has two fields: Name and Type. In order to control the nature of the declaration that is generated, users must follow the following conventions:
- The Name field must contain the simple name of the entity being declared, i.e., the part that appears before the colon in the Ada declaration. No colon or semicolon may appear in the Name field.
- The Type field must contain anything that appears after the colon in the Ada declaration. However, no initial value must be specified. Instead, the code generation property InitialValue must be used if an initial value is to be generated for the declaration. No colon or semicolon may appear in the Type field.
The following examples demonstrate how to use these fields, and what is the corresponding Ada declaration:
- Variable:
Name: Foo Type: Integer- Constant:
Name: Foo Type: constant Boolean- Named Number:
Name: Foo Type: constant- Exception:
Name: Foo Type: exception- Renaming:
Name: Foo Type: Integer renames BarEvaluating the Generated Code
After you have located the generated files, you evaluate them to determine whether to use them as generated. Based on your evaluation, you may decide to regenerate the code after refining the model, adjusting the values of code-generation properties, or both.
Use the information provided in the rest of this chapter to guide your evaluation. Each section lists some of the things you can change about a particular aspect of code generation.
Completing the Implementation of the Generated Code
When you are satisfied with the way code is generated from your model, you complete the code by implementing the package bodies. If you did not use the Ada Generator to create stubbed bodies, you can select the specifications in Apex, and choose the Build Body command from the Compile menu. Rational recommends, however, that you let Rose generate code for the bodies, since it will produce the appropriate code regions.
To complete the implementation of your code, you may insert additional statements and/or declarations in the preserved code regions. A preserved code region is a special block of comments starting with --## and containing the clause preserve=yes. Preserved code regions are preserved by the code generator the next time the code is regenerated. This makes sure that you may continue evolving your model in Rose after you have started refining the implementation of the code. Note that some of the code regions that Rose generate have preserve=no, so if you want them preserved, you must change this clause to preserve=yes.
You cannot add your own code regions: if you try to do this, they will be considered orphaned by the code generator (see below). You must use the code regions produced by the Ada Generator. Here is a list of the code regions that the Ada Generator produces:
- The module.cp region, which appear at the beginning of the unit, contains the text found in the property CopyrightNotice of a Module Spec/Body. This region may be preserved if the region is modified manually.
- The module.withs region, which follows the with clauses and precedes the compilation unit, may be used to insert additional with clauses, use clauses, or pragmas.
- The module.declarations region, which occurs at the beginning of the package visible part and at the beginning of the package body, may be used to insert additional declarations.
- The module.additionalDeclarations region, which occurs at the end of the package visible part and at the end of the package body, may be used to insert additional declarations.
- The module.privateDeclarations region, which occurs at the beginning of the private part, may be used to insert additional declarations.
- The module.additionalPrivateDeclarations region, which occurs at the end of the private part, may be used to insert additional declarations.
- The module.statements region, which covers the statement part of the package body, may be used to insert statements which are executed at elaboration time. By default, the statement part of any package body contains a single null statement.
- The class_name.operation_name%context.id.declarations region, which covers the declarative part of each generated subprogram, and may be used to insert declarations in the subprogram body. The name of this section is generated by Rose from the class name, the operation name, and various other pieces of information that help disambiguate the identity of the subprogram.
- The class_name.operation_name%context.id.statements region, which covers the statement part of each generated subprogram, and may be used to insert statements in the subprogram body. The name of this section is generated by Rose from the class name, the operation name, and various other pieces of information that help disambiguate the identity of the subprogram.
Regenerating Code
You can regenerate code for a given set of class items by following the same steps you used to generate the original code. When you regenerate code into existing files, the current contents of these files are saved in backup files before the new contents are written. By default, each backup file has the extension .1.ad~ or .2.ad~, as appropriate. The same backup files are overwritten each time you regenerate code to the same source-code files. The regenerated files:
- Reflect any changes you made to the model or to properties.
- Contain any code regions you edited in the previously generated version of the files, provided that the preserve keyword for each region was set to yes.
Note that if you delete or rename a notation item for which a code region was preserved, that region is "orphaned" when you regenerate code. This means that the Ada Generator places the code region in a special section at the end of the regenerated file so that you can decide whether to reuse any of the edits you made in that region. The Ada Generator automatically changes the preserve keyword to no in orphaned regions, so that they are discarded the next time you regenerate the file.
Refining the Subsystem and View StructureDetermining the Directory for an Ada File
There are several properties which the Ada Generator uses when determining the directory for an Ada file, if Apex is available:
- The project properties Directory and ApexView
- The UML package properties ApexSubsystem and ApexView
The directory for a module is based on the concatenation of the project Directory property, and the UML package's ApexSubsystem and ApexView properties. Modules must be contained within component UML packages.
The directory for a class which has been assigned to a module is determined by applying these rules to its assigned module. The directory for a class which has not been assigned to a module is based on the UML package to which it is assigned: if it is enclosed in a logical UML package which is assigned to a component UML package, its directory is created from the ApexSubsystem and ApexView properties for the component UML package. If ApexSubsystem is blank, the subsystem name is set to the name of the component UML package.
If it is enclosed in a logical UML package which is not assigned to a component UML package, its directory is created from the default values of ApexSubsystem and ApexView properties, plus the project Directory property. If the default ApexSubsystem property is blank, the subsystem name is set to the name of the logical UML package.
If Apex is not available, a hierarchy of directories is created using the name of the component UML packages (if they exist) or of the logical UML packages (in the absence of component UML packages).
Mapping Classes and Modules to Ada Units
By default, each class is assigned to an implicit module specification. From these implicit modules, the Ada Generator produces a package specification containing the class definition. The units are generated according to the values in the default module-spec property set.
To change the default mapping from classes to units, you may either change the class name, or assign two or more classes to the same module, as follows:
- 1 Introduce component diagrams into your model.
- 2 Create a module specification for each Ada specification you want to generate.
- 3 Assign each class to the appropriate module via the class's specification: to generate a package specification, you assign the class to a module specification. To generate the code for multiple classes in a single package, you assign each class to the same module.
Specifying Filenames
The name of a generated file has two parts: a name and an extension, separated by a period (for example, foo.1.ada). The name is generated automatically, and the extension is controlled by different code-generation properties. If you are using Rational Apex, you should not change these values.
When a file is generated from a module, the filename is determined by the name of the module: it is the same as the module name, except in lowercase.
In the default case where classes are mapped to implicit modules, each implicit module assumes the name of the corresponding class. Consequently, each generated filename is based on the implicit module name (and, indirectly, on the class name).
To specify a non-default file name for a generated class, introduce a component diagram, if necessary, and assign the class to a module specification with the desired name.
Refining Class Definitions (Ada 83)The Ada Generator creates a type declaration for each selected class. The format of the type depends on the following property values:
- Class Name
- Discriminant
- Implementation Type
- Is Subtype
- Is Task
- Variant
See Code Generation Properties for more information on each property.
Standard Operations
Standard operations are subprogram declarations that are commonly found in Ada classes. They include:
- Default constructor
- Copy constructor
- Destructor
- Equality operation
By default, each class is generated with a default constructor, copy constructor, and destructor. Class properties permit you to specify the kind (procedure or function) and name for some of these standard operations.
Note that you can overload a standard operation by setting the relevant class property to cause it to be generated, and then specifying one or more additional operations with the same name, but different parameters in the class specification.
User-Defined Operations
User-defined operations are subprogram declarations that are generated from the operations you define in a class specification. Note that you do not need to define standard operations in a class specification, unless you want to overload them (see above).
If you want additional subprogram declarations for a class, or if you want different arguments or return types, you must edit the class specification.
One operation property, ClassParameterMode, permits you to specify the parameter mode of the class parameter, which is included automatically.
Get and Set Operations
Get and set operations are subprogram declarations that provide access to components. By default, a pair of get and set operations are generated from each "has" relationship, providing the relationship is public.
You can suppress the generation of a get and set operations by blanking-out the GetName and SetName properties in the property set that is attached to the has relationship. To define your own get and set functions, you define them as you would any other user-defined operation in the class specification.
Inherited Operations
When one class (called a subclass) inherits another class, all of the visible user-defined, get, and set operations defined in the superclass get replicated in the package specification of the subclass. This is how Ada 83 can achieve inheritance: the data is inherited by adding a field to the record, and the operations are inherited by replicating them in the subclass definition.
When you implement the body of an inherited operation, you typically do nothing except call the operation of the inherited class with record field that matches that class. If you do anything else, you are overriding that operation.
Record Fields and Object Declarations
Record fields are generated from "has", association and generalization relationships and attributes defined in diagrams or in specifications. (If you have set the static adornment on the "has" relationship, an object declaration in the private part of the package specification is generated.
The component type is determined by a number of factors. By default, the type is determined by a combination of the supplier class and the multiplicity and containment of the "has" relationship.
In the simplest cases, the component type is:
- The class name of the supplier class for a one-to-one by-value relationship.
- The handle name of the supplier class for a one-to-one by-reference relationship.
In more complex cases (maximum allowable cardinalities larger than 1), the Ada Generator inserts a container class for the component type, which you can either use as generated or replace with the name of a container class of your own.
For bounded containers, the Ada Generator creates an array declaration in the private part of the class package specification.
For unbounded containers, the Ada Generator instantiates a container generic package in the private part of the package specification.
You replace these default container classes by setting the various Container class properties.
Specifying Additional Ada Unit ContentsYou can tailor aspects of the structured comments and context clauses that appear at the beginning of the generated Ada units. You can also cause the Ada Generator to generate visible declarations at the beginning of one or more units.
Adding Structured Comments
The Ada Generator inserts a block of structured comments at the beginning of each generated file. You can set properties to generate a copyright notice string in these comments.
In the default case where classes are mapped to implicit modules, you edit properties in the default module-spec property set, which is attached to the implicit modules. If you have explicitly assigned classes to modules, you must edit each property set that is attached to a module.
Adding With Clauses
By default, the Ada Generator produces with clauses in units based on class relationships and module dependencies in your model. If you want additional with clauses to appear in one or more generated files, use one of the following methods, as appropriate.
If you want more generated units to reference each other in with clauses, you can inspect the relationships among existing items in the model to determine whether you have represented them adequately.
For example, you may find that you need to add a uses relationship from one class to another, which will cause a with clause to be generated in the first class's Ada unit. (A with clause is generated only if the classes are generated in different units.)
Similarly, you can introduce dependencies among modules in a module diagram, which result in generated with clauses.
If you want any of the generated units to reference units that are not among the generated units, you can use the AdditionalWiths property to insert additional with clauses to reference those units.
If you want to put a special with clause in just one or two generated units, you can do so by editing these units directly. To do this, you insert the desired with clauses between these source markers at the beginning of the unit:
--##begin module.withs preserve=yes --##end module.withsAdding Global Declarations
You can cause the Ada Generator to generate global declarations before the first class definition in a unit. To do this, you:
- 1 Introduce a module diagram, if necessary, and assign one or more classes to a module specification (or body, as appropriate).
- 2 Double-click on the module specification to bring up its specification.
- 3 Enter the desired declaration(s) in the Declarations box. The text you enter here will be inserted at the beginning of the generated unit.
Rational Software Corporation
http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2001, Rational Software Corporation. All rights reserved. |