6.2.5.32. Architecture-EdgeInterpretation

Replace each non-hierarchical edge in a view by a (possibly empty) set of edges, depending on the edge type, its stereotype, and its target multiplicity

Required inputs: RFG

Summary

Replaces edges A->B in an RFG view by edges of other edge type. These replacement edges also have the form A->B, or, if specified, B->A (see is_reversed option later). Such an edge replacement is also called interpretation.

The configuration of these replacements can be set via two options simple_interpretations and interpretations. simple_interpretations is less flexible but simpler to use. This configuration can modified directly from the axivion_config GUI. However, for complex use cases, interpretations exists and can be set via Python. use_simple_interpretations determines which of the two options should be used. See the configuration description of simple_interpretations for details about it. The remainder of this text describes interpretations.

The way in which the interpretation is carried out is specified by config option interpretations. In most cases the replaced edges are of type UML Relation and originate from an import of a UML-based CASE tool model (like Enterprise Architect or Rhapsody). In order to be processed in the architecture check, these relations have to be interpreted as dependencies that occur in the source code (source dependencies), like e.g. dependencies of type

  • Source_Dependency, which represent arbitrary source dependencies,
  • or
  • Call, which represent calls in the code, but e.g. do not include dependencies originating from variable accesses.
It depends on the intended meaning of case tool relations by which source dependencies the CASE tool relations have to be replaced. The interpretation rules can differ from project to project and depend on many factors, like the constructs in the programming language of the code base, the requirements of the project, and the individual modelling rules defined by the software architect. The rule has an input source_view_name (the name of the imported architecture view source_view) and an input target_view_name (the resulting interpreted architecture view target_view). After invocation, the view named target_view_name contains all nodes already contained in source_view, and the edges created by the interpretation; the originating edges are removed.

Default Interpretation

The default value of interpretations handles edge types which are subtypes of UML Relation and which occur often in common CASE tool imports. It replaces then by edges of type Source_Dependency in the target view. Edges of type Rhapsody To/From/ToPort/FromPort/AllTag/Stereotype are removed from the target view (no replacement given).

Example Interpretations

A simple and common interpretation is to replace all edges of type UML Dependency by Source Dependency. This can be done by the following interpretations setting:

{
    'UML Dependency': [
        (
            {},
            [
                ('.*', [{'edge_type': 'Source_Dependency'}]),
            ],
        )
}
One can restrict the edge type to Call - then other source accesses between two software components only connected by a UML Dependency in the originating model are reported as divergences:
{
    'UML Dependency': [
        (
            {},
            [
                ('.*', [{'edge_type': 'Call '}]),
            ],
        )
    ]
}
It is possible to make the dependencies optional (resulting edges have the Architecture.Is_Optional attribute set):
{
    'UML Dependency': [
        (
            {},
            [
                ('.*', [{'edge_type': 'Call ', 'is_optional': True}]),
            ],
        )
    ]
}
It is also possible to generate more than one edge (or no edge at all):
{
    'UML Dependency': [
        (
            {},
            [
                ('.*', [{'edge_type' : 'Call ', 'is_optional ': True}, {'edge_type ': 'Use_Of_Type ', 'is_optional ': True}]),
            ],
        )
    ]
}
and to give interpretations for different edge types:
{
    'UML Dependency': [
        (
            {},
            [
                ('.*', [{'edge_type': 'Source_Dependency', 'is_optional': True}]),
            ],
        )
    ],
    'UML InformationFlow': [
        (
            {},
            [
                ('.*', [{'edge_type': 'Call ', 'is_optional': True, 'is_reversed' : True}]),
            ],
        )
    ],
}
Here the rule given for edge type UML InformationFlow generates edges of type Call that are reversed (i.e., source and target nodes are swapped), by specifying a true value for the option is_reversed.

Details of interpretation specifications

Besides discriminating by edge types, the format of interpretations allows to distinguish further between multiplicity and stereotype information. In the following this is explained by a more complex example: an architecture model is chosen that uses the UML relations Dependency and Realisation for modeling relations between architectural entities of a C++ or C# system:

  • An edge A -> B of type UML Dependency without annotated stereotypes shall represent that a source code element mapped to entity A is allowed to access each source code element in entity B in any possible way (including calls, reading and setting members, including files, using types etc.).
  • An edge A -> B of type UML Dependency annotated with the stereotype Client shall represent that entities in A are allowed to call routines in B and are allowed to read global variables defined in B; no other source relations are allowed.
  • If the multiplicity of an edge A -> B of type UML Dependency is given as 1 for the source and 0..* for the target, there does not necessarily have to be a corresponding actual dependency between entities mapped to A and entities mapped to B in the code.
  • An edge A -> B of type UML Realisation shall represent that A is an instance of B, i.e., A and B are mapped to classes such that the class corresponding to A inherits from B.
In this case, the option interpretations might look as follows (please be aware that the edge types referring to UML relations in the RFG have the prefix 'UML'):
{
    'UML Dependency': [
        (
            {'source': ['1'], 'target': ['0..*']}, # multiplicity constraint
            [                                      # list for different stereotype conditions
                (None, [{'edge_type ': 'Source_Dependency', 'is_optional': True}]), # no stereotype
                (
                    'Client ',  # stereoype Client
                    [  # list of dictionaries describing edge types and attributes
                        {'edge_type': 'Call', 'is_optional': True},
                        {'edge_type': 'Variable_Use', 'is_optional': True},
                    ],
                ),
            ],
        ),
        (
            {}, # empty multiplicity. constraint
            [
                (None, [{'edge_type': 'Source_Dependency', 'is_optional': False}]),
                (
                    'Client',
                    [
                        {'edge_type': 'Call', 'is_optional': False},
                        {'edge_type': 'Variable_Use', 'is_optional': False},
                    ],
                ),
            ],
        ),
    ],
    'UML Realisation': [
        (
            {},
            [
                (
                    '.*',
                    [
                        {'edge_type': 'Extend'},
                        {'edge_type': 'Override'},
                    ],
                )
            ],
        )
    ],
}
In general, interpretations represents a decision tree: the rule chooses the concrete interpretation for a given architecture edge by checking the type of the edge, then potential multiplicity conditions, then potential annotated stereotypes.
  • A multiplicity condition is given as a dictionary with potential keys 'source' and 'target', having a list of strings as values; see comment # multiplicity constraint in the example. If one (or both) of these conditions is given, the tool checks whether the multiplicities of the architectural edge (given by the attributes UML.Source_Multiplicity resp. UML.Target_Multiplicity in the RFG) matches one of the elements in the corresponding list. If an empty dictionary is given (see line with '# empty mult. constraint' comment in the example), no check takes place and the edge automatically satisfies the condition.
  • The stereotype conditions are given as the first components of a list, see comment #list for different stereotype conditions in the example. Each of the components of such a list is processed in sequence. The value None for a stereotype condition matches only for edges having no stereotype attached. The wildcard '.*' matches all edges (with or without attached stereotype). All other values (like 'Client' in our example) match every edge having the stereotype with exactly the same name attached (regular expressions can also be used).
  • Each dictionary following in the nested list (see # list of dictionaries describing edge types and attributes in the example) corresponds to an edge of the interpretation that has to be created. Therefore they require the key 'edge_type' to be defined; optionally, boolean values for the keys 'is_optional' and 'is_reversed' can be given (all other keys are ignored). If 'is_optional' is specified, the attribute Architecture.Is_Optional is attached to the newly created edge; if 'is_reversed' is specified, the direction of the edge is reversed (i.e., it points from B to A, see example before).
If no interpretation could be found for a (non-hierarchical) edge, a warning is issued, and the original edge is copied into the newly created view.

Possible Messages

This rule has no predefined messages.

Options

export_warnings

export_warnings : bool = True

Export output messages to the analysis database.
 

interpretations

interpretations

Type: typing.Any

Default:

{
   'Rhapsody AllTag': [({}, [(None, []), ('.*', [])])],
   'Rhapsody From': [({}, [(None, []), ('.*', [])])],
   'Rhapsody FromPort': [({}, [(None, []), ('.*', [])])],
   'Rhapsody Stereotype': [({}, [(None, []), ('.*', [])])],
   'Rhapsody To': [({}, [(None, []), ('.*', [])])],
   'Rhapsody ToPort': [({}, [(None, []), ('.*', [])])],
   'UML Aggregation': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Association': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Composition': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Delegate': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Dependency': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Generalisation': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Generalization': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML InformationFlow': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Instance': [({}, [(None, []), ('.*', [])])],
   'UML Nesting': [({}, [(None, []), ('.*', [])])],
   'UML Realisation': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Realization': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])],
   'UML Usage': [({}, [(None, [{
                     'edge_type': 'Source_Dependency'
                  }]), ('.*', [{
                     'edge_type': 'Source_Dependency'
                  }])])]
}
More powerful version of the simple_interpretations attribute. use_simple_interpretations must be False for interpretations to have any effect.
 

loglevel

loglevel : LogLevel = 'WARNING'

Logging mode. WARNING only outputs errors and warnings, INFO additionally prints info messages, DEBUG additionally outputs info and debug messages.
 

simple_interpretations

simple_interpretations

Type: dict[str, EdgeMapping]

Default:

{
   'Rhapsody AllTag':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with=None,
      report_absence=False,
      reverse=False
   ),
   'Rhapsody From':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with=None,
      report_absence=False,
      reverse=False
   ),
   'Rhapsody FromPort':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with=None,
      report_absence=False,
      reverse=False
   ),
   'Rhapsody Stereotype':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with=None,
      report_absence=False,
      reverse=False
   ),
   'Rhapsody To':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with=None,
      report_absence=False,
      reverse=False
   ),
   'Rhapsody ToPort':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with=None,
      report_absence=False,
      reverse=False
   ),
   'UML Aggregation':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Association':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Composition':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Delegate':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Dependency':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Generalisation':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Generalization':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML InformationFlow':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Instance':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with=None,
      report_absence=False,
      reverse=False
   ),
   'UML Nesting':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with=None,
      report_absence=False,
      reverse=False
   ),
   'UML Realisation':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Realization':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   ),
   'UML Usage':    bauhaus.rules.axivion.architecture.edge_interpretation.EdgeMapping(
      match_with='Source_Dependency',
      report_absence=False,
      reverse=False
   )
}
Simplified version of the interpretations attribute that can be edited directly in the GUI. use_simple_interpretations must be True for simple_interpretations to have any effect
 

source_view_name

source_view_name : str = 'EA Architecture'

Name of the existing source view.
 

target_view_name

target_view_name : str = 'Architecture'

Name of the target view to be generated.
 

use_simple_interpretations

use_simple_interpretations : bool = False

Should the configuration from simple_interpretations be used instead of interpretations?
 

Option Types

These types are used by options listed above:

AxivionEdge

An enumeration.
 
  • Call

  • Communication

  • Declare

  • Extend

  • Grant_Friendship_To

  • Implementation_Of

  • Include

  • Inheritance

  • Override

  • Source_Dependency

  • Variable_Use

EdgeMapping

EdgeMapping(match_with: Union[bauhaus.rules.axivion.architecture.edge_interpretation.AxivionEdge, str, NoneType] = None, report_absence: bool = False, reverse: bool = False)
 

match_with : AxivionEdge | str | None = None

The name of the edge type in the Axivion suite onto which this edge should be matched. Can be None in which case the edge is not matched at all.
 

report_absence : bool = False

Whether the analysis should report an absence error, if there is no item in the analyzed code that fulfills this edge.
 

reverse : bool = False

Whether the matched edge should be reversed. If the model has an edge A->B, the reversed edge is B->A.
 

LogLevel

An enumeration.
 
  • WARNING

  • INFO

  • DEBUG