Rational Software Corp.

TOC PREV NEXT INDEX



Mapping the UML Notation to Ada 95 -- Code Generation


Contents

This chapter is organized as follows:


Introduction

This chapter details the forward-engineering mapping between the UML notation and the Ada 95 programming language.

Roughly speaking, classes are transformed into types declared in library packages, utilities are transformed into library packages, attributes and relationships are transformed into record components. The main source of information for the code generation are the class diagrams. Code generation properties may be used to gain finer control over the way that code is produced. If component diagrams are present, some of the information they contain is also used by the code generator.

Because UML and Ada use the word "package" to designate two different concepts, this document uses the phrase "UML package" for a package in the UML acceptation, and the word "package" without qualification for an Ada package. When necessary, the phrases "logical UML package" and "component UML package" are used to refer to UML packages in the logical view or in the component view, respectively.


Name Space

This section defines how the naming of entities in the UML notation corresponds to the naming of declarations in the generated Ada 95 code.

The following rules define the legal names for entities of a model that is used to generate Ada 95 code:

In other words, a class name must either be an Ada simple name, an Ada expanded name, or a pseudo-expanded name (an expanded name followed by a colon and an identifier: this is called the colon notation hereafter).

The code generator checks the legality of names, in particular in terms of consistency with the Ada Reference Manual.

From the name of a class the code generator derives the name of a library-level package (the package where the type and operations associated with the class are declared) and the name of a type (the type associated with the class) as follows:

The code generation property TypeName defaults to "Object".

These rules support two different approaches to naming the classes in the Rose model: either the class name reflects the hierarchy of units, or the class name is for design purposes only, and the hierarchical unit structure is defined using the mapping to modules. In the former case, the colon notation may be used to make the type names explicit in the class diagram. Alternatively, the type names may be specified using the property TypeName.

For utilities, similar rules are used, except that there is no type declaration, so the TypeName property is irrelevant, and the colon notation is not allowed.

Note that it is possible for several classes to map to types declared in the same Ada package, either by using the colon notation, or by using associations between classes and modules. However, such a mapping is only legal if all classes that map to a given module are part of the same UML package. In the case of associations between classes and modules, the correspondence between logical and component UML packages ensure that the mapping is always legal. In the case of the colon notation, the legality of the mapping is checked by the code generator.


Name Resolution

While a large part of the information in a model is entered graphically by means of relationships and adornments, there are a number of situations where the user enters textually in the model a piece of information which designates a class. Examples of such situations include the definition of the type of attributes or parameters.

The code generator performs name resolution to determine the Ada type to be generated in these circumstances. To explain how the name resolution works, consider the case of class A having an operation Op with a parameter (or result) type written as "B". The code generator performs the following operations:

Note that this resolution mechanism applies regardless of whether the parameter type is a simple name (like "B"), an expanded name (like "B.C") or a colon notation (like "B:T" or "B.C:T"). If the parameter type uses the colon notation, it will only match a class name that also uses the colon notation. In all cases, the generated code references the type name, not the class name.

It may be that there are ambiguities, for instance if the parameter type is given as "B" and the set of target classes includes classes named "B:T1" and "B:T2". In this case, an error message is emitted, and the parameter type has to be made more explicit.

This name resolution mechanism makes it possible to use class names everywhere in the model, and defer the mapping of class names to Ada type names by setting the TypeName code generation property and/or the mapping of classes to modules. Changing the mapping of classes to types and modules doesn't require to change the attributes, parameters, etc., scattered throughout the model.

Of course, the user may always enter an Ada type name for the type field of an attribute or parameter, since such a name will not match any class name, and will thus be copied verbatim in the generated code. This may be useful for predefined types like Integer or Calendar.Time, for which it would be cumbersome to create a class in the model. However, it is strongly recommended that class names, not type names, be used wherever possible in order to ease maintenance of the model if the mapping of classes to types ever has to change.


Code Generation Properties and Consistency

Various entities in a model have associated code generation properties which may be used to control the way that the code is produced. Often, there exist consistency requirements between the values of the code generation properties of one or several entities.

These requirements come most of the time from language rules, and ensure that the generated code is correct. To take an example, in Ada 95, it is not possible to specify, when declaring a derived type, if it is limited: it just inherits its limited-ness from the root of the derivation tree. In Rose/Ada, the code generation property IsLimited may be used to control whether or not the type generated for a given class is limited. Clearly, it does not make sense for a root class A to have IsLimited set to False, and for a class B, subclass of A, to have IsLimited set to True.

In practice however, having to set code generation properties in a consistent manner over large models may become burdensome. To avoid this, some code generation properties are said to be dominant over others. A dominant property determines the code generated, and the dominated property is ignored altogether, even if it specifies a different code generation. For instance, if a root class has IsLimited set to True, the code generation property IsLimited of its subclasses is not even considered: these classes will all be limited.

In some circumstances, a property is dominant only when it has a specific value (or set of values). For instance, the property TypeImplementation dominates IsLimited only when it has the values Task or Protected (because task types and protected types are always limited).

One may however wish to be able to track and correct inconsistencies where, for instance, IsLimited is set to True on the root class but to False on some of its subclasses, Such inconsistencies may turn out to be a problem in organizations having strict quality assurance policies. To ease detection of inconsistencies, the code generator emits a warning message whenever it detects that a dominated property has a value which is inconsistent with the dominant property.


Classes

If a "normal" class is associated with a module, that module must be a non-generic package.

Normally, the type generated to represent objects of the class is a non-limited, private type. This can be controlled using the code generation properties IsLimited and TypeVisibility attached to the class:

The scheme used to generate the code associated with a class is governed by the code generation properties TypeImplementation and TypeDefinition.

If TypeDefinition is not empty, it dominates TypeImplementation, and the type generated uses the contents of that property (technically, the contents of TypeDefinition must be an Ada type definition). If for instance TypeDefinition is set to "range -1 .. 3" then the generated type declaration is:

type Object is range -1 .. 3;

If TypeDefinition is empty (the default), TypeImplementation is used to control the code generation scheme. TypeImplementation can take one of five values: Tagged, Record, Mixin, Task or Protected. In the rest of this section, we consider each of these schemes in turn. In this discussion, unless otherwise specified we assume the default values for properties IsLimited and TypeVisibility.

Tagged Implementation

The class corresponds to a tagged type. If the class has no superclass, the declaration of the corresponding type is:

type Object is tagged private;

If the class has a superclass, the declaration of the corresponding type is:

type Object is new Superclass.Object with private;

If the class has more than one superclass, we are in a situation of multiple inheritance, which is covered later.

If the class is abstract, the associated type declaration includes the reserved word abstract:

type Object is abstract tagged private;

type Object is abstract new Superclass.Object 
                                     with private;

Record Implementation

In this scheme, polymorphism (if any) is implemented using records with variants. This means that if the class has any subclass, an enumeration type is created to represent all possible variants, and the record type declaration associated with the class is a variant record gathering the attributes and relationships of all the subclasses.

The properties TypeImplementation and IsLimited of the root class dominate those of the subclasses. Also, none of the classes may be marked abstract.

There are two ways that the record mapping can be implemented, so the Record scheme is further controlled by the code generation property RecordImplementation associated with the root class. This property can take the two values SingleType and MultipleTypes. The property RecordImplementation of the root class dominates the same property for its subclasses.

Regardless of the mapping chosen, for a class which has no superclass and no subclasses, the generated code is simply (assuming the default values for the properties TypeVisibility and IsLimited):

package A is
    type Object is private;
private
    type Object is
        record
            ...
        end record;
end A;

When discussing the two possible record implementations in more complex cases, we'll use the following generalization hierarchy as an example:

SingleType Record Implementation

In this scheme, a single record type is created for the complete generalization hierarchy. An enumeration type is created that lists all the variants, and the structure of the record corresponds to that of the generalization tree. For each subclass, a package is created that declares a subtype or derived type with a discriminant constraint (depending on the property IsSubtype). For leaf classes, the discriminant is omitted. The code generated is as follows:

package A is    -- The root package
    type A_Kinds is (Some_A, Some_B, Some_C, 
                            Some_D, Some_E, Some_F);
    type Object (Kind : A_Kinds := Some_A) is private;
private
type Object (Kind : A_Kinds := Some_A) is
        record
            Ca : Integer;
            case Kind is
               when Some_B =>
                    Cb : Integer;
               when Some_C | Some_D | Some_E | Some_F =>
                    Cc : Integer;
                    case Kind is
                        when Some_D =>
                            Cd : Integer;
                        when Some_E | Some_F =>
                            Ce : Integer;
                            case Kind is
                                when Some_F =>
                                    Cf : Integer;
                                when others =>
                                    null;
                            end case;
                        when others =>
                            null;
                    end case;
               when others =>
                    null;
            end case;
        end record;
end A;
with A;
package B is                -- A leaf
    type Object is private;
private
    type Object is new A.Object (A.Some_B);
end B;
with A;
package C is        -- An intermediate node
    subtype C_Kinds is A.A_Kinds 
                            range A.Some_C .. A.Some_F;
    type Object (Kind : C_Kinds := A.Some_C) is private;
private
    type Object (Kind : C_Kinds := A.Some_C) is 
                            new A.Object (Kind);
end C;

The prefix used to generate the names of the enumeration literals is specified using the code generation property EnumerationLiteralPrefix of the class. This property defaults to "A_". In the above examples, we have assumed for readability that it was set to "Some_".

Note that the code generator orders the enumeration literals in a way that is suitable for the constraints on subtype Kinds in the intermediate nodes.

The property TypeVisibility of the root class dominates the same property for subclasses.

The SingleType mapping may result in name conflicts: if two components of two classes in a generalization hierarchy have the same name, they will clash when they are put together in the above record type declaration. It is the user's responsibility to avoid such conflicts.

MultipleTypes Record Implementation

In this scheme, one record type is created for each class in the hierarchy, and these types are aggregated in a discriminated record at each level, according to the structure of the generalization hierarchy. For subclasses, a subtype or derived type with a discriminant constraint is created (depending on the property IsSubtype). For leaf classes, the discriminant is omitted.

package A_Record_Kind is
    type A_Kinds is (Some_A, Some_B, Some_C,
                            Some_D, Some_E, Some_F);
end A_Record_Kind;
with A_Record_Kind;
with B;
with C;
package A is
    use A_Record_Kind;
    subtype A_Kinds is A_Record_Kind.A_Kinds;
    type Object (Kind : A_Kinds := Some_A) is private;
private
type Object (Kind : A_Kinds := Some_A) is
        record
            Ca : Integer;
            case Kind is
               when Some_B =>
                    The_B : B.Object;
               when Some_C | Some_D | Some_E | Some_F =>
                    The_C : C.Object (Kind);
               when others =>
                    null;
            end case;
        end record;
end A;
package B is
    type Object is private;
private
    type Object is
        record
            Cb : Integer;
        end record;
end B;
with A_Record_Kind;
with D;
with E;
package C is
    use A_Record_Kind;
    subtype C_Kinds is A_Kinds range Some_C .. Some_F;
    type Object (Kind : C_Kinds := Some_C) is private;
private
    type Object (Kind : C_Kinds := Some_C) is
        record
            Cc : Integer;
            case Kind is
               when A_Record_Kind.Some_D =>
                    The_D : D.Object(Kind);
               when A_Record_Kind.Some_E |
                      A_Record_Kind.Some_F =>
                    The_E : E.Object(Kind);
               when others =>
                    null;
            end case;
        end record;
end C;

As before, the prefix used to generate the names of the enumeration literals is specified using the code generation property EnumerationLiteralPrefix of the root class, which was set to "Some_" in the above example. Also, the prefix used to generate the names of the intermediate record components is given by the code generation property RecordFieldPrefix of the root class (this property defaults to "The_").

Finally, the name of the auxiliary package used to declare the enumeration type Kinds is given by the code generation property RecordKindPackageName of the root class. This property defaults to "${class}_Record_Kinds".

Mixin Implementation

A class whose TypeImplementation property is set to Mixin must be abstract. If that class has no superclass (see figure), the following code is generated:

generic
    type Superclass is abstract tagged private;
package A is
    type Object is abstract new Superclass with private;
    -- declaration of the operations
    -- of the class here.
private
    type Object is new Superclass with
        record
            -- declaration of the attributes
            -- and relationships
            -- of the class here.
        end record;
end A;

If the class has (exactly one) superclass, B, then B must have its TypeImplementation property set to Tagged (see figure), and the generic formal part above is changed as follows:

with B;
generic
    type Superclass is abstract new B.Object with private;
package A is ...

Classes implemented according to the Mixin scheme are used in multiple inheritance situations as explained later on.

Task Implementation

A class whose TypeImplementation property is set to Task must not be abstract, and its code generation property IsLimited is dominated. Also, its operations must all be procedures (as opposed to functions). A task type is generated for such a class.

The operations are transformed into entries, and their SubprogramImplementation property is dominated. Depending on the visibility of each operation, the entry is declared either in the visible part or in the private part of the task type. No implicit parameter is ever generated for an operation in the Task mapping, because the implicit parameter is the task itself: TypeImplementation dominates ImplicitParameter.

For each visible operation of the class, a procedure is also generated in the visible part of the package that declares the task type. This procedure has the same profile as the corresponding entry of the task, except for an additional parameter that designates the object being operated upon. The name of this additional parameter is given by the code generation property ImplicitParameterName of the class. The body of each of these procedures simply calls the corresponding entry of the given task object.

The attributes and "has" relationships whose property RecordFieldImplementation is either Discriminant or AccessDiscriminant are transformed into discriminants, as for any composite type. The attributes and "has" relationships whose property RecordFieldImplementation is Component, and the associations, are transformed into variables declared in the task body.

Accessor operations (Get and Set) are never generated for attributes of a class whose TypeImplementation property is Task (in other words, GenerateGet and GenerateSet are dominated).

An example of code generated for the Task mapping is as follows:

package A is
    type Object (D : Integer := 0) is limited private;
    procedure Op1 (This : Object);
private
    task type Object (D : Integer := 0) is
        entry Op1;
    private
        entry Op2;
    end Object;
end A;
with B;
package body A is

    procedure Op1 (This : Object) is
    begin
        This.Op1;
    end Op1;

    task body Object is
        Attr1 : Float;
        Attr2 : Boolean := False;
        Aggr1 : B.Object;
        Aggr2 : B.Handle;
        ...
    end Object;

end A;

Classes implemented according to the Task mapping cannot be used in generalization relationships.

Protected Implementation

A class whose TypeImplementation property is set to Protected must not be abstract, and its code generation property IsLimited is dominated. A protected type is generated for such a class.

The operations are transformed into protected functions or protected procedures, except that an operation whose concurrent behavior is specified as synchronous is transformed into an entry. The code generation property EntryBarrierCondition of such an operation contains the boolean expression used for the barrier of the entry body. This property defaults to "True".

Depending on the visibility of each operation, it is declared either in the visible part or in the private part of the protected type. No implicit parameter is ever generated for an operation in the Protected mapping, because the implicit parameter is the protected object itself: TypeImplementation dominates ImplicitParameter.

For each visible operation of the class, a subprogram is also generated in the visible part of the package that declares the task type. This subprogram has the same profile as the corresponding protected subprogram, except for an additional parameter that designates the object being operated upon. The name of this additional parameter is given by the code generation property ImplicitParameterName of the class. The body of each of these subprograms simply calls the corresponding protected subprogram of the given protected object.

The attributes and "has" relationships whose property RecordFieldImplementation is either Discriminant or AccessDiscriminant are transformed into discriminants, as for any composite type. The attributes and "has" relationships whose property RecordFieldImplementation is Component, and the associations, are transformed into components of the protected object (and are thus declared in the private part).

An example of code generated for the Protected mapping is as follows:

package A is
    type Object (D : Integer := 0) is limited private;
    procedure Op1 (This : Object);
    function Op2 (This : Object) return Integer;
private
    protected type Object (D : Integer := 0) is
        entry Op1;
        function Op2 return Integer;
    private
        procedure Op3;
        Attr : Float;
        Aggr1 : B.Object;
        Aggr2 : B.Handle;
    end Object;
end A;

with B;
package body A is

    procedure Op1 (This : Object) is
    begin
        This.Op1;
    end Op1;

    function Op2 (This : Object) return Integer is
    begin
        return This.Op2;
    end Op2;

    protected body Object is
        entry Op1 when Attr > 0.0 is
        begin
            ...
        end Op1;
        function Op2 return Integer is
        begin
            ...
        end Op2;
        procedure Op3 is
        begin
            ...
        end Op3;
    end Object;

end A;

Classes implemented according to the Protected mapping cannot be used in generalization relationships.


Parameterized Classes

There exist two mappings for parameterized classes: either as types declared in generic units, or as types with unconstrained discriminants. Correspondingly, there exist two mappings for bound classes: generic instantiations and constrained types. The mapping is selected by the code generation property ParameterizedImplementation: if this property is set to Generic (the default), the "generic" mapping is used, if it is set to Unconstrained the "unconstrained type" mapping is used.

In all cases, if a parameterized class is associated with a module, the code generation property ParameterizedImplementation must be consistent with the nature of the associated module: if ParameterizedImplementation is Generic, the associated module must be a generic package, if it is Unconstrained it must be a non-generic package.

If a class is parameterized, all its subclasses must also be parameterized. The property ParameterizedImplementation of a root class dominates the same property for its subclasses.

Generic Implementation

The root class is transformed into a type declared in a generic library package. The exact nature of the type is controlled by the property TypeImplementation, as for normal classes. The formal part of the generic is extracted from the class specification.

Subclasses are transformed into a tagged type declared in a generic library package, but we have two cases to consider:

Unconstrained Type Implementation

The discriminant part of the type is derived from the class parameters. Each class is transformed into a type having unconstrained discriminants (without default values). For a subclass, type derivation is used to add discriminants without constraining the discriminants inherited from the parent type.

If one any of the parameters has a type of the form "access T" then the property IsLimited is dominated, and a limited type is generated for the class.

An example of code generated for the Unconstrained Type implementation is as follows (assuming the default values for other code generation properties):

package A is
    type Object (D1 : Integer; D2 : access String) is
                                            tagged limited private;
    ...
end A;
with A;
package B is
    type Object (D1 : Integer;
                  D2 : access String;
                  D3 : Boolean) is new A.Object (D1, D2)
                                                        with private;
    ...
end B;


Bound Classes

If a bound class is associated with a module, that module must be a non-generic package.

The value of ParameterizedImplementation for a parameterized class (Generic or Unconstrained) determines the mapping chosen for any bound class obtained by binding the parameters of that parameterized class. In other words, the property ParameterizedImplementation of a parameterized class dominates the same property for the bound classes.

Generic Implementation

The class is transformed into a library-level generic instantiation. The actual parameters are extracted from the class specification.

Consider a bound class B1 obtained by binding the parameters of a parameterized class P1. Say that P1 is not a root class, but has instead a superclass P2. Because the actual parameters of B1 only specify values for the parameters of P1, and not of P2, there must exist a bound class B2, obtained by binding the parameters of a parameterized class P2, from which B1 "inherits" the actual parameters for P1.

The UML notation does not allow inheritance relationships between bound classes, because bound classes are fully specified by their template. Therefore, the pseudo-inheritance between B1 and B2 is represented by a dependency relationship labelled "parent", as shown on the diagram below:

Based on this information, the code is generated in two different ways depending on whether P1 had visibility over its ancestor by a parent-child relationship or by a formal package (see above):

package B1 is new B2.P1 (...);
package B1 is new P1 (Superclass => B2, ...);

Unconstrained Type Implementation

The class is transformed into a type declaration that provides discriminant constraints. Alternatively, a subtype is generated if the boolean code generation property IsSubtype for the class is True (this property defaults to False).

Each bound class must provide values for all parameters (i.e., constraints for all discriminants), including those inherited from the generalization hierarchy.

An example of code generated for the Unconstrained Type implementation is as follows (assuming the default values for other code generation properties):

package C is
    subtype Constrained_Object is
            B.Object
                (D1 => 3,
                 D2 => Some_String'Access,
                 D3 => False);
    type Object is Constrained_Object with private;
    ...
private
    type Object is Constrained_Object with
        record
            ...
        end record;
end C;


Utilities

If an utility is associated with a module, that module must be a non-generic package or subprogram. If an utility is not associated with a module, it is transformed into a package. Similarly, parameterized utilities are transformed into generic units, and bound utilities are transformed into library-level instantiations.

If an utility is transformed into a package, no type declaration is produced. Instead, each operation of the utility is transformed into a subprogram in that package. Attributes of such an utility become package-level declarations, regardless of the setting of the "static" button.

If an utility is transformed in a subprogram, then the utility must declare exactly one operation. Note that a bound utility must map to the same kind of program unit as its template.


Metaclasses

A metaclass must not have any associated module. The attributes and operations it declares are instead used to generate code for classes that derive from that metaclass.

A metaclass attribute or relationship is transformed into a variable or constant. Depending on the visibility of the attribute or relationship, the variable is declared in the visible part (public), the private part (protected or private) or the body (implementation) of the package associated with each class that derives from the metaclass.

A metaclass operation is transformed into a subprogram, which is declared in the same package as each class which derives from the metaclass. Each parameter (or result) of such a subprogram which had a type name identical to that of the metaclass is transformed into a class-wide parameter. Depending on the visibility of the operation, the subprogram is declared in the visible part (public), the private part (protected or private) or the body (implementation) of the package associated with each class that derives from the metaclass.

An example of code generated for metaclasses is as follows. Note that no module is generated for the metaclass A. Also note the difference between class attributes and operations on one hand, and metaclass attributes and operations on the other hand:

package B is

    type Object is tagged private;
    procedure Q (This : Object);

    X : Integer;
    procedure P (This : Object'Class);

private
    type Object is tagged
        record
            Y : Float;
        end record;
end B;


Attributes

An attribute is generally transformed into a record component. There exists two special cases for the generation of attributes: the attributes of a metaclass are transformed into package-level declarations, as explained above. The attributes of a normal class which are marked as "static" are also transformed into package level declarations. In fact, in term of code generation, static attributes are handled exactly as attributes of metaclasses.

The record component corresponding to an attribute has a name which is given by the code generation property RecordFieldName.

The code generated for an attribute is controlled by the code generation property RecordFieldImplementation. This property can take the values Discriminant, AccessDiscriminant, and Component (the default). For a parameterized class whose ParameterizedImplementation is Unconstrained, the property RecordFieldImplementation is dominated, and all attributes are implemented as components. If a class has, in its generalization hierarchy, an attribute implemented as an AccessDiscriminant, then the property IsLimited is dominated, and a limited type is generated for that class.

The semantics of RecordFieldImplementation is as follows:

All attributes (and "has" relationships; see below) whose RecordFieldImplementation property is either Discriminant or AccessDiscriminant must agree on the existence of default values, and on the visibility: either all have defaults, or none have defaults, and they all have the same visibility. In addition, if the code generation property TypeImplementation of the class is Tagged, then it dominates the property InitialValue, and no default value is generated.

The discriminants always appear in the full type declaration. For private types, whether or not the discriminants appear in the private type declaration depends on their visibility and on the existence of defaults:

The case of a class inheriting discriminants from its superclass (and possibly adding new discriminants) is handled in a manner similar to the Unconstrained Type mapping of parameterized classes.


Has Relationships

"Has" relationships are not part of the UML notation. However, they can be created in Rose using the View > As Booch option. When viewed using the UML or OMT notation, they are displayed as unidirectional aggregation relationships. However, they have slightly different code generation properties than true aggregations, because they gather together the properties borne by associations and the properties borne by roles.

An "has" relationship is generally transformed into a record component. There exists two special cases for the generation of "has" relationships: the relationships of a metaclass are transformed into package-level declarations, as explained above. The relationships of a normal class which are marked as "static" are also transformed into package level declarations. In fact, in term of code generation, static "has" relationships are handled exactly as "has" relationships of metaclasses.

In the rest of this discussion, we consider the case of class A having a "has" relationship to class B.

The mapping of an "has" relationship depends on whether it is by-value or by-reference:

The access type used to represent by-reference relationships targeting B is declared in the package associated with class B. Its name is given by the code generation property AccessTypeName of class B (this property defaults to "Handle"). It is generated either in the public part or in the private part, based on the code generation property AccessTypeVisibility, which can take the values Public (the default) and Private.

If the code generation property AccessTypeDefinition of B is not empty, it dominates, and the declaration of the access type uses this property. Technically, AccessTypeDefinition must contain an Ada type definition. For instance, if AccessTypeDefinition is set to "access constant B.Object" the access type is declared as follows:

type Handle is access constant B.Object;

If the code generation property AccessTypeDefinition of B is empty (the default), an access type is generated as follows:

There may be circumstances where it is useful to have an access type declaration generated for class B, even though B is not (or not yet) the target of any by-reference "has" relationship. The code generation property GenerateAccessType controls the generation of an access type. It can take the values Auto and Always. The default is Auto, and corresponds to the case where the generation of the access type depends on the existence of a by-reference "has" relationship. The value Always force the generation of an access type declaration, regardless of the existence of by-reference "has" relationships.

If the maximum allowable cardinality of the relationship is 1, the type of the record component representing the relationship is directly the object or access type associated to B, as explained above.

If, on the other hand, the maximum allowable cardinality of the relationship is larger than 1, an intermediate container type is required to support the one-to-many relationship. The scheme used to generate the code associated with a one-to-many relationship is governed by the code generation properties ContainerImplementation and ContainerType.

If ContainerType is not empty, it dominates ContainerImplementation, and specifies the container type used to represent the one-to-many relationship. The code generation property ContainerDeclarations may be used to specify auxiliary declarations that may be necessary to build the container type.

If ContainerType is empty (the default), ContainerImplementation is used to control the code generation scheme. ContainerImplementation can take the two values Generic and Array, and defaults to Array. The semantics of this property is as follows:

The code generation property RecordFieldImplementation which was discussed above in the context of attributes can also be applied to "has" relationships, with the same semantics, except that AccessDiscriminant is not allowed for a by-value relationship.

Note that the target of a "has" relationship must not be a class whose TypeImplementation property is Mixin.

As an illustration of the implementation of "has" relationship, consider the following class diagram:

It results into the following code (note that only the "get" accessors are shown; the "set" accessors have similar parameter types):

with B;
 with List_Generic;
 package A is
     type Object (Has5 : access B.Object) is tagged limited private;

     package B_List is new List_Generic (B.Object);

     function Get_Has1 (This : in Object) return B.Object;
     function Get_Has2 (This : in Object) return B.Handle;
     function Get_Has3 (This : in Object) return B.Array_Of_Object;
     function Get_Has4 (This : in Object) return B_List.List;

     -- "set" accessors go here

 private
     type Object (Has5 : access B.Object) is tagged limited
         record
             Has1 : B.Object;
             Has2 : B.Handle;
             Has3 : B.Access_Array_Of_Object;
             Has4 : B_List.List;
         end record;
 end A;

 package B is
     type Object is tagged private;
     type Handle is access all Object'Class;
     type Array_Of_Object is
        array (Positive range <>) of Object;
     type Access_Array_Of_Object is
        access Array_Of_Object;
 private
        ...
 end B;

The following defines the generic container used by Has4:

generic
    type Item is private;
package List_Generic is
    type List is tagged private;
private
    ...
end List_Generic;


Associations

Associations fall into two categories:

The generated code for both categories follows a number of common principles.

Code is only generated for the roles which are marked as navigable in the Rose model. If an association has no navigable role, no code is generated for that association.

Code is only generated if the two classes that participate in the association have their Type Implementation property set to Record or Tagged. An error is emitted if an association involves classes with a non-record, non-tagged implementation.

There exist many similarities between the mapping of associations and that of "has" relationships:

Simple Associations

If a simple association has only one navigable role, the code generated for that association is exactly identical to the code that would be generated for an "has" relationship similar to that role. Such an association may be marked "static", in which case package-level declarations are generated instead of record components (again, this is identical to the case of an "has" relationship).

A warning is emitted by the code generator when it encounters a unidirectional association, because an association normally has two navigable roles (and thus the presence of only one navigable role may indicate a mistake).

The rest of this section pertains only to the case of a simple association with two navigable roles.

The two classes which participate in the association must map to the same package, either because their names use the colon notation and have the same prefix, or because they are associated with the same module (a package specification).

In both classes the TypeVisibility property must be set to Private.

An association may have keys which are used to unambiguously identify an object. Keys are handled by Rose/Ada exactly as attributes of classes: they are normally generated as record components, possibly with "get" and "set" accessors. If several associations originating from the same class declare keys with the same name, the record component is only generated once. An error is detected in this case if the various keys don't have the same type.

A bidirectional association may not be marked "static".

Data Structures

If any role of a bidirectional association is by-value, an error is detected.

If both roles of a bidirectional association are by-reference, the data structures (record, components, discriminants, etc.) generated for the association are exactly identical to the data structure that would be generated for two by-reference "has" relationships. These data structures depend on the multiplicity of the association. They are shown below, assuming that both classes use the Tagged implementation, and that arrays are used to represent relationships with maximum allowable cardinality larger than 1.

In the following examples, the AccessTypeName class property must be given a unique name since both classes map to the same package.

package A is

    type T1 is tagged private;
    type H1 is access T1'Class;

    type T2 is tagged private;
    type H2 is access T2'Class;

    -- Operations go here

private

    type T1 is tagged
        record
            -- Keys and attributes go here
            Y : H2;
        end record;

    type T2 is tagged
        record
            -- Keys and attributes go here
            X : H1;
        end record;

end A;
package A is

    type T1 is tagged private;
    type H1 is access T1'Class;

    type Array_Of_H1 is
                  array (Positive range <>) of H1;
    type Access_Array_Of_H1 is access Array_Of_H1;

    type T2 is tagged private;
    type H2 is access T2'Class;

    -- Operations go here

private

    type T1 is tagged
        record
            -- Keys and attributes go here
            Y : H2;
        end record;

    type T2 is tagged
        record
            -- Keys and attributes go here
            X : Access_Array_Of_H1;
        end record;

end A;
package A is

    type T1 is tagged private;
    type H1 is access T1'Class;

    type Array_Of_H1 is 
                  array (Positive range <>) of H1;
    type Access_Array_Of_H1 is access Array_Of_H1;

    type T2 is tagged private;
    type H2 is access T2'Class;

    type Array_Of_H2 is 
                  array (Positive range <>) of H2;
    type Access_Array_Of_H2 is access Array_Of_H2;

    -- Operations go here

private

    type T1 is tagged
        record
            -- Keys and attributes go here
            Y : Access_Array_Of_H2;
        end record;

    type T2 is tagged
        record
            -- Keys and attributes go here
            X : Access_Array_Of_H1;

        end record;

end A;

Subprograms

A "get" accessor may be generated for each role in the association, based on the code generation properties GenerateGet, GetName and InlineGet of the role.

Bidirectional associations must be created and deleted using the subprograms Associate and Dissociate as explained below. This is for integrity reasons: if two objects are linked by a bidirectional association, it is important that each of them has a pointer to the other. If "set" accessors were generated in that case, they could be used to create a situation where object A has a pointer to object B, but object B doesn't have a pointer to object A. Such a situation doesn't correspond to an association, but to two aggregation relationships. By generating Associate and Dissociate subprograms instead of "set" accessors for bidirectional associations, Rose/Ada prevents such violations of the association model.

Two families of subprograms, named Associate and Dissociate by default, may be generated for each role, under the control of the code generation properties GenerateAssociate and GenerateDissociate of the association. These subprograms are used to establish or break an association by establishing or breaking linkages between objects. The profiles of these subprograms depend on the multiplicities of both roles, and on the nature of the construct used to implement relationships with maximum allowable cardinality larger than 1. The code shown below corresponds to the case where the ContainerImplementation property of the roles is Array. If the ContainerImplementation is Generic, or if a container type is provided, the name of the container type is substituted to the name of the array type in the subprogram declarations.

Alternate names may be provided for the Associate and Dissociate subprograms using the code generation properties AssociateName and DissociateName. The code generation properties InlineAssociate and InlineDissociate control whether or not a pragma Inline is emitted for these subprograms.

procedure Associate
              (This_H2 : in H2; This_H1 : in H1);

procedure Dissociate (This_H2 : in H2);
procedure Dissociate (This_H1 : in H1);

Note that for associations having a role whose maximum allowable cardinality is 1, Associate never replaces the current association, if it turns out that the object on that role is already part of some association. Instead, the exception System.Assertion_Error is raised. On the other hand, for a role whose maximum cardinality is unlimited, it is always possible to augment the current association, so no exception is ever raised.

If replacement is needed for an association, it may be implemented by successively calling Dissociate and Associate.

If the Association and Dissociate subprograms are passed null pointers, they raise System.Assertion_Error. However, for the versions of these subprograms which take arrays of access values, it is acceptable for the arrays to contain null pointers: these null pointers are simply skipped. Still, the entire array must contain at least one non-null pointer.

For one-to-one associations, and for one-to-many or many-to-many associations with ContainerImplementation properties set to Array, the bodies of the Associate and Dissociate procedures are entirely generated by Rose/Ada, with the semantics explained above. They perform storage management by reusing empty slots in the arrays, allocating longer arrays if needed, and reclaiming storage when appropriate. They also preserve the integrity of the association by detecting the case where two of the access passed to Associate denote the same object. Because the generated code is part of a protected region, it can be modified by the user to meet special needs. It is however recommended that the above semantics be adhered to.

For one-to-many or many-to-many associations with a specific ContainerType, or with a ContainerImplementation set to Generic, the bodies of the Associate and Dissociate procedures are left empty.

Association Classes

For an association class, independent objects must be created to hold the attributes of the association. Therefore, a type is generated which corresponds to the association class. This type may be generated in any package: it doesn't have to be located in the same package which contains the two principal classes involved in the association.

Data Structures

The generated data structures are similar to what would be generated if the association class had a one-to-many association with each of the two principal classes. However, these data structures are essentially hidden, and the clients are only given operations to query, create or delete the association, and operations to read or modify the attributes of the association. This ensures that the integrity of the association is preserved.

The data structures are such that, from each end of the association, it is possible to find a list of auxiliary records. Each of these auxiliary records contains a value of the association class, and two pointers to both ends of the association. So it is possible to traverse from one end of the association to the other through the auxiliary record. The auxiliary record and the associated type declaration are not exported, to preserve the integrity of the association.

The generated data structures for a many-to-many association class are as follows:

package B is
    type T is tagged private;

    -- Operations go here

private
    type T is tagged
        record
            -- Attributes go here
        end record;
end B;

with B;
package A is

    type T1 is tagged private;
    type H1 is access T1'Class;
    type Array_Of_H1 is array (Positive range <>) of H1;
    type Access_Array_Of_H1 is access Array_Of_H1;

    type T2 is tagged private;
    type H2 is access T2'Class;
    type Array_Of_H2 is array (Positive range <>) of H2;
    type Access_Array_Of_H2 is access Array_Of_H2;


-- Operations go here

private

    type Attribute_B is
        record
            Attribute : B.T;
            The_T1 : H1;
            The_T2 : H2;
        end record;

    type Access_Attribute_B is access Attribute_B;

    type Array_Of_Access_Attribute_B is
            array (Positive range <>) of Access_Attribute_B;
    type Access_Array_Of_Access_Attribute_B is
            access Array_Of_Access_Attribute_B;

    type T1 is tagged
        record
            -- Keys and attributes go here
            The_B : Access_Array_Of_Access_Attribute_B;
        end record;

    type T2 is tagged
        record
            -- Keys and attributes go here
            The_B : Access_Array_Of_Access_Attribute_B;
        end record;

end A;

Similar code would be generated in the one-to-one and one-to-many cases.

Subprograms

Associate and Dissociate procedures are generated for the entire association. These procedures are similar to those corresponding to a simple association, except for that only one Associate procedure is generated, regardless of the multiplicity. That's because it is mandatory to specify, when establishing an association, the value of the association class. The variants of the Associate subprogram that would take array of accesses for the principal classes would also have to take array of values for the association class. This interface would be complex and difficult to use, so it is not supported by Rose/Ada.

Two accessor subprograms are generated to read and modify the value of the attributes of the association class. In order to determine the association to modify, these subprograms take:

That information makes it possible to unambiguously locate the association whose attributes must be read or modified. The generation of the "get" accessor is controlled by the properties GenerateGet, GetName and InlineGet of the association. Similarly the generation of the "set" accessor is controlled by the properties GenerateSet, SetName and InlineSet of the association.

The generated subprograms for an association class are shown below (we omit the Dissociate procedures which are exactly identical to those generated for simple associations):

As in the case of simple associations, Rose/Ada generates a full implementation for these subprograms if the roles with maximum allowable cardinality larger than 1 are represented by arrays. It generates a [statement] prompt otherwise. This implementation checks the consistency of the operations, and raises System.Assertions_Error if inconsistencies are detected. It also performs storage management, allocating and reclaiming the arrays and auxiliary records as appropriate.


Dependency Relationships

A dependency relationship between two classes is transformed in a with clause between the corresponding library units, unless of course both classes happen to map to types in the same library unit. Note that in addition to dependency relationships, with clauses are also generated from the module dependencies appearing in the component diagrams.


Generalization Relationships (Inheritance)

To some extend, the generalization relationship has already been discussed in the section about classes above.

The visibility of a generalization relationship is used to determine how the type derivation is declared. If the relationship is public, the derivation occurs in the visible part, with a private extension:

package Subclass is
    type Object is new Superclass.Object with private;
private
    type Object is new Superclass.Object with 
        record ... end record;
end Subclass;

If the relationship is not public, the derivation occurs in the private part:

package Subclass is
    type Object is tagged private;
private
    type Object is new Superclass.Object with 
        record ... end record;
end Subclass;

If the class Subclass has its code generation property TypeVisibility set to Public, then regardless of the visibility of the relationship, the code is simply:

package Subclass is
    type Object is new Superclass.Object with 
        record ... end record;
end Subclass;

The case of multiple inheritance is more complex. If a class A has more than one superclass, there are two ways that this relationship can be represented in Ada 95: "mixin" inheritance or "multiple views" inheritance. The code generation properties TypeImplementation of the superclasses of A determine what mapping is used.

Mixin Inheritance

In mixin inheritance, exactly one of the superclasses of A must have its code generation property TypeImplementation set to Tagged. This superclass defines the "main" line of inheritance (or generalization). All other superclasses must have their code generation property TypeImplementation set to Mixin.

The type representing A is declared by deriving from its main superclass, and instantiating the generic packages associated with the mixin superclasses to add more primitive operations to the resulting type. Assume that the main superclass is called A1 and the mixin superclass A2. The generated code is as follows, assuming that A1 and A2 each declare an operation (we use the defaults for those code generation properties that have no direct bearing on multiple inheritance):

package A1 is
    type Object is tagged private;
    procedure Op1 (This : Object);
private
    type Object is tagged
        record ... end record;
end A1;

generic
    type Superclass is abstract tagged private;
package A2 is
    type Object is abstract new Superclass with private;
    procedure Op2 (This : Object);
private
    type Object is abstract new Superclass with 
        record ... end record;
end A2;
with A1;
with A2;
package A is
    package A2_Instantiation is
        new A2 (Superclass => A1.Object);
    type Object is new A2_Instantiation.Object with 
                                            private;
    procedure Op (This : Object);
private
    type Object is new A2_Instantiation.Object with
        record ... end record;
end A;

The case of triple inheritance and beyond is handled similarly, with more instantiations adding more primitive operations. Assuming that we add a mixin superclass, A3, to the above example, we obtain the following code (A1 and A2 are unchanged):

generic
    type Superclass is abstract tagged private;
package A3 is
    type Object is abstract new Superclass with private;
    procedure Op3 (This : Object);
private
    type Object is abstract new Superclass with 
        record ... end record;
end A3;

with A1;
with A2;
with A3;
package A is
    package A2_Instantiation is
        new A2 (Superclass => A1.Object);
    package A3_Instantiation is
        new A3 (Superclass => A2_Instantiation.Object);
    type Object is 
        new A3_Instantiation.Object with private;
    procedure Op (This : Object);
private
    type Object is new A3_Instantiation.Object
        with record ... end record;
end A;

Note a constraint on mixin inheritance: if any of the mixins has a superclass, it is necessary for the "main" superclass to be a specialization of the same class (otherwise the instantiation would be illegal). This means that the following diagram is illegal because B is not identical to A and is not a subclass of A:

In the case of triple inheritance and beyond, this rule becomes slightly more complicated: all the mixins must either have no superclass, or have the same superclass, and the main class must be identical to this common superclass, or inherit from it.

Multiple Views Inheritance

In multiple views inheritance, all the superclasses of A must have their code generation property TypeImplementation set to Tagged. In addition, one of the inheritance (or generalization) relationships must be identified as the main line of descent by giving it the name "main".

There are a number of restrictions on multiple views inheritance. First, all superclasses must be limited, by setting their code generation property IsLimited to True (or because IsLimited is dominated by another property which forces limited-ness). Second, the main inheritance relationship cannot be "less visible" than the auxiliary relationships. For instance, it is not possible to have a private main inheritance and a public auxiliary inheritance. On the other hand, it is possible to have only private inheritance, or to have a public main inheritance, a public auxiliary inheritance, and another, private, auxiliary inheritance.

All the operations of the superclasses are inherited, and default bodies are generated if necessary. If two operations coming from different superclasses would result in homograph declarations for the class A, the operation coming from the main line of inheritance has precedence.

Assuming that the main superclass is called A1 and the auxiliary superclass is called A2, the following code is generated (again, we use the defaults for those code generation properties that have no direct bearing on multiple inheritance):

package A1 is
    type Object is tagged limited private;
    procedure Op1 (This : Object);
private
    type Object is tagged limited
        record ... end record;
end A1;

package A2 is
    type Object is tagged limited private;
    procedure Op2 (This : Object);
private
    type Object is tagged limited
        record ... end record;
end A2;
with A1;
with A2;
package A is
    type Views;

    type A2_With_Back_Pointer
        (Back : access Views'Class) is
        new A2.Object with null record;

    type Views is abstract new A1.Object with
        record
            A2_View : A2_With_Back_Pointer (Views'Access);
        end record;

    type Object is new Views with private;
    procedure Op (This : Object);
    procedure Op2 (This : Object);
private
    type Object is new Views with
        record ... end record;
end A;

The body of subprogram Op2 is generated as follows, in order to call the corresponding subprogram of the superclass:

procedure Op2 (This : Object) is
begin
    A2.Op2 (A2.Object (This.A2_View));
end Op2;

The same scheme extends to triple inheritance and beyond. If we add superclass A3, we obtain:

package A3 is
    type Object is tagged limited private;
    procedure Op3 (This : Object);
private
    type Object is tagged limited
        record ... end record;
end A3;

with A1;
with A2;
with A3;
package A is
    type Views;

    type A2_With_Back_Pointer
        (Back : access Views'Class) is
        new A2.Object with null record;
    type A3_With_Back_Pointer
        (Back : access Views'Class) is
        new A3.Object with null record;

    type Views is abstract new A1.Object with
        record
            A2_View : A2_With_Back_Pointer (Views'Access);
            A3_View : A3_With_Back_Pointer (Views'Access);
        end record;

    type Object is new Views with private;
    procedure Op (This : Object);
    procedure Op2 (This : Object);
    procedure Op3 (This : Object);
private
    type Object is new Views
        with record ... end record;
end A;

The interaction with the visibility of inheritance relationships is worth expressing in detail. In the first case, if the inheritances from A1 and A2 are changed to be private (or protected), we don't need the intermediate type Views anymore, and the code generated for A becomes:

with A1;
with A2;
package A is
    type Object is tagged limited private;
    procedure Op (This : Object);
private
    type A2_With_Back_Pointer
        (Back : access Object'Class) is
        new A2.Object with null record;

    type Object is new A1.Object with
        record
            A2_View : A2_With_Back_Pointer (Object'Access);
            ...
        end record;
    procedure Op2 (This : Object);
end A;

In the case of triple inheritance, if the visibility of the inheritance from A3 is changed to private (or protected) the generated code for A becomes:

with A1; with A2; with A3;
package A is
    type Views;

    type A2_With_Back_Pointer
        (Back : access Views'Class) is
        new A2.Object with null record;

    type Views is abstract new A1.Object with
        record
            A2_View : A2_With_Back_Pointer (Views'Access);
        end record;

    type Object is new Views with private;
    procedure Op (This : Object);
    procedure Op2 (This : Object);
private
    type A3_With_Back_Pointer
        (Back : access Object'Class) is
        new A3.Object with null record;
    type Object is new Views with
        record
            A3_View : A3_With_Back_Pointer (Object'Access);
            ...
        end record;
    procedure Op3 (This : Object);
end A;


Operations

The operations given in a class specification are simply copied in the generated code.

If the code generation properties ImplicitParameter of the project and of the class are both True, a first parameter may be added to the profile of each operation. The type of this parameter is the type associated with the given class, its mode is given by the code generation property ImplicitParameterMode of the operation, and its name is given by the code generation property ImplicitParameterName of the class. These properties default to In and "This", respectively.

The code generation property ImplicitParameter at the project level defaults to False. The code generation property ImplicitParameter of the class defaults to True. By having two code generation properties, one at the project level and one at the class level, Rose/Ada supports the following usage patterns:

The code generation property ImplicitParameterMode can take the values In, InOut and Out. There are also circumstances in which it is useful to generate a subprogram taking an access parameter in addition (or instead of) the subprogram taking an object parameter. The code generation property GenerateAccessOperation controls whether a subprogram taking an access parameter is generated. This property is only used if ImplicitParameter is True.

Accessor Operations

Each attribute, "has" relationship, and association role has two code generation properties, GenerateGet and GenerateSet, which control generation of accessor operations for this attribute or relationship. These properties default to False.

For attributes and "has" relationships which are translated into discriminants, the "set" accessor doesn't make sense, and is therefore not generated (in other words, GenerateSet is dominated by RecordFieldImplementation). The "get" accessor is not generated either, because a discriminant is directly visible to clients, even for a private type: GenerateGet is also dominated by RecordFieldImplementation in this case.

In addition to (or instead of) the "get" and "set" accessors which take object parameters, Rose/Ada can also generate accessors which take access parameters. This is controlled by the code generation properties GenerateAccessGet and GenerateAccessSet.

The boolean code generation properties InlineGet and InlineSet of the attribute, relationship or role control whether a pragma Inline is generated for the accessor operations. These properties default to True.

Standard Operations

Standard operations, not explicitly present in the model, may be generated if the code generation property GenerateStandardOperations of the project is set to True (it defaults to False):

If an access type is generated for the class (in addition to the true object type), and the class is not abstract, then the above properties also control generation of the subprograms pertaining to this access type. For instance, if GenerateCopyConstructor is set to Function, and CopyConstructorName is set to "Copy", two Copy functions are generated: one for the object type, and one for the associated access type. This rule only applies to the subprograms described in this section: it doesn't apply to "get" and "set" accessors, or to user-defined subprograms.

On an abstract class, the above subprograms, if generated, are made abstract.

Note that making the constructors functions (as opposed to procedures) on classes which map to limited types may lead to difficulties, and is not recommended (although it may make sense in some circumstances).

The boolean code generation properties InlineDefaultConstructor, InlineDestructor, InlineCopyConstructor and InlineEquality of the class control whether a pragma Inline is generated for the above operations. All these properties default to False.

Subprogram Implementation

The code generation property SubprogramImplementation is used to control the code generated for a subprogram body. This property can take the values Body, Renaming, Separate, Abstract and Spec. The default is Body. The semantics of these choices are as follows:

In addition, the code generation property Inline is used to control whether or not a pragma Inline is generated for the operation. This property defaults to False.

Visibility

The visibility of each operation determines where it is declared. A public operation is declared in the visible part of the associated package, a protected or private operation is declared in the private part of the package, and an operation with only implementation visibility is declared in the package body (note that such an operation is not inherited).

Overriding

The code generator takes care to generate the proper overriding subprogram declarations whenever the language requires it:

In addition to these cases where overriding is required by the language, the code generator also generates an overriding declaration if the inherited operation has it code generation property GenerateOverriding set to True. This property defaults to True.

Each overriding subprogram declaration has the same parameter names, modes and default values as that of the original subprogram. The proper type name is substituted for each controlling operand. The types of other operands are left unchanged.

Rose/Ada generates a body for each overriding subprogram declaration. This body does a view conversion of its controlling parameters, and calls the corresponding operation of the parent type (or superclass). While this implementation in itself is not extremely useful, it turns out that most overridden subprograms first call the operation of their parent type, and then perform additional processing specific to the added record components. By generating the call to the superclass' operation, Rose/Ada makes it easy to adhere to this model. (This is similar to sending a message to super in languages like Smalltalk or Java.)

Note that there is not property GenerateOverriding for the "get" and "set" accessor. That's because most of the time the inherited implementation is appropriate. Therefore, no overriding declaration is ever generated for these accessors.

Bodies

Except for the accessor operations, the body generated for an operation contains only a [statement] prompt. This ensures that the code can be compiled under Rational Apex, but that any attempt to execute an operation whose body is incomplete raises Program_Error. Note that, if using another compiler, the prompt is likely to result in syntax errors: legal code must be written to replace these dummy bodies before the code can be compiled.

The code generation properties EntryCode and ExitCode associated with an operation contain Ada statements which are copied verbatim at the beginning and at the end, respectively, of the statement part of the generated body. These properties are empty by default.


User-Defined Initialization, Assignment and Finalization

Controlled types may be produced for any type whose TypeImplementation is Tagged. In addition to producing the proper type structure, Rose/Ada is also capable of generating overriding declarations for the procedures Initialize, Adjust and Finalize, and for the operator "=".

The code generation property TypeControl of a class may take the following values:

TypeControl defaults to None. For a class whose TypeImplementation is not Tagged, TypeControl is dominated, and the generated type is not a controlled type. A class whose TypeControl property is not None must not be involved in a multiple inheritance relationship.

When discussing the effect of TypeControl, we'll use the following class hierarchies as examples:

If TypeControl is not None, the declaration of the type associated with a class is changed as follows:

If the code generation property TypeControl is set to AssignmentFinalizationOnly or to All, overriding declarations are inserted for Adjust and Finalize in the private part of the package, and a declaration for the operator "=" is inserted in the visible part. Adjust is only declared if IsLimited is False:

package A is
    type Object is tagged private;
    function "=" (Left, Right : in Object) return Boolean;
private
    type Object is ...
    procedure Adjust (What : in out Object);
    procedure Finalize (What : in out Object);
end A;

with B;
package C is
    type Object is new B.Object with private;
    function "=" (Left, Right : in Object) return Boolean;
private
    type Controlled_Object is ...
    procedure Adjust (What : in out Controlled_Object);
    procedure Finalize (What : in out Controlled_Object);
    type Object is new B.Object with ...
end C;

In the declaration of procedures Initialize, Adjust and Finalize, the name of the only parameter is given by the code generation property ImplicitParameterName for the class. In the declaration of operator "=", the parameters are named Left and Right.

The code generation property TypeControl, when it is not None, dominates the properties GenerateDefaultConstructor, DefaultConstructorName, GenerateCopyConstructor, CopyConstructorName, GenerateDestructor, DestructorName, GenerateTypeEquality and TypeEqualityName: no standard operation is generated, and the name of the equality operator, when it is generated, is always "=". This is because standard operations and controlled types are two different mechanisms to achieve similar effects, and they are not intended to coexist in a single class.

GenerateGet and GenerateSet may be used in conjunction with controlled types: the accessor operations which are generated correctly take into account the internal structure of the type (possibly with an auxiliary controlled type) to access the various components.

A class whose code generation property TypeControl is not None may be abstract. However, the auxiliary controlled type (if generated) is never made abstract, and the Initialize, Adjust and Finalize procedures (if generated) are not made abstract either.


Rational Software Corporation  http://www.rational.com
support@rational.com
techpubs@rational.com
Copyright © 1993-2001, Rational Software Corporation. All rights reserved.
TOC PREV NEXT INDEX