7.4.3. Class View

A view is a subgraph of an RFG. You can load and save views as GXL files, query the attributes of the view, check if nodes and edges are contained in a view, and add and remove nodes and edges. You can compute union, difference, and intersection of views. You can lift edges in a hierarchical view. The view itself grants access to the neighborhood of nodes and edges, i. e. one can query nodes and edges based on the view. You can get a set of all nodes and edges contained in the view. A view can be iterated, e. g. using a for loop in which case the iterated elements are the nodes contained in the view.

code_facts_view_name()
environment_view_name()
module_view_name()
lifted_module_view_name()
call_view_name()
user_view_name()
entries_view_name()
alive_view_name()
dead_view_name()
mapping_view_name()
architecture_view_name()
reflexion_view_name()
causing_view_name()
clones_view_name()
cycles_view_name()
dominance_tree_view_name()

Some view names imply a special meaning of the view. The name Code Facts for example is used for views that contain the elements that model the source code of the system under analysis.

Caution

These routines return just the fixed default view names used for the given purposes, independent of the loaded RFG. The view names created by a specific analysis setup may differ.

Note

Gravis consults the view attribute Gravis.ROLE to find the default input views for architecture analysis. For instance, the following code finds the view with the Architecture role if there is one, else the result is None:

architecture_view = \
    next((v for v in graph.xviews() \
            if 'Gravis.ROLE' in v \
                and v['Gravis.ROLE'] == 'ARCHITECTURE_ROLE'), \
         None)

Apart from ’ARCHITECTURE_ROLE’, other useful roles are ’BASE_ROLE’ (identifying the Base view), ’HIERARCHY_ROLE’ (Hierarchy view) and ’MAPPING_ROLE’ (Mapping view). See Architecture Analysis Facilities for an explanation of these roles.

7.4.3.1. Obtaining a View

class View(graph, viewname[, filename, format[, link_edges[, import_attributes[, merge_attributes]]]])

The constructor of class View used with a graph object graph and a view name view_name as arguments has the following functionality: If no view named view_name exists in graph, it creates a new view named view_name in graph and returns it. Otherwise it returns the already existing view named view_name in graph.

The constructor can also be used to import a view from an external file into graph. For this you have to supply at least the third argument filename, which denotes the location of the file to be imported. In this case, the parameter view_name specifies the name of the view in which the content of the file should be imported: if no view with name view_name exists in the graph, a view named view_name is created and the file content is imported into this view, which is then returned. If a view with name view_name already exists in the graph, a fresh view name is constructed, and the file content is imported into this new view, which is then returned. So view_name is not the name of the returned view in this case.

  • The parameter format (default: ’GXL’) specifies the format of the imported file. Only the GXL format is supported right now, so the format specification (if given) must always be ’GXL’.

  • The parameter link_edges (default: True) specifies whether edges of same type and the same source / target nodes shall be linked together during import.

  • The parameter import_attributes (default: True) specifies whether attributes of nodes and edges shall be imported. If the option is set to False, edges are not linked, i. e. multiple edges of the same type in the file will lead to multiple edges in the graph (which is not the default behavior).

  • The parameter merge_attributes (default: True) specifies whether imported attributes shall be merged (i.e., existing values are preferred). If the option is set to False, they are overwritten (i.e. values of existing ones are replaced by imported values).

7.4.3.2. Storing a View

save(filename, format[, attributes])

The instance method save saves the view to a file named filename. If the file exists, it is overwritten silently. If the position is not writable or otherwise invalid, an exception is raised describing the details why the operation failed. The format specification must be “GXL”.

One can additionally request that attribute values should not be stored by way of the optional parameter attributes. ’True’ (default) saves attribute values, ’False’ does not store attribute values. Only ’Linkage.Name’ and ’Source.Name’ are stored in any case.

The example in Listing Saving and loading a view stores the Code Facts view of an RFG as GXL file and loads the file immediately creating a new view named Copy of and the name of the Code Facts view.

Saving and loading a view
from bauhaus import rfg
my_graph = rfg.Graph(filename)
my_view = my_graph.view(rfg.code_facts_view_name())
my_view.save(filename+"-CODEFACTS.gxl", "GXL")
loaded_view = View(
    my_graph,
    "Copy of"+rfg.code_facts_view_name(),
    filename+"-CODEFACTS.gxl",
    "GXL")

7.4.3.3. Destroying a View

A view in an RFG graph by itself is not subject to garbage collection. You have to take actively measures to get rid of it.

destroy()

The method destroy removes the view from the graph.

Caution

Unlike removing every element (node, edge) from a view, completely destroying the view not necessarily destroys all elements not contained in other views of the graph. Nonetheless you are required not to use any node or edge you obtained from a destroyed view.

7.4.3.4. General Operations

graph()

Instance method graph returns the graph the view is part of.

__str__()

Instance method __str__ returns a textual representation of the view (only for debugging purposes). For user output, use name and the attribute handling methods.

__copy__()
__deepcopy__()

Shallow copies are not allowed, so __copy__ throws a TypeError exception. Instance method __deepcopy__ makes a deep copy of the view, see copy at Section View Operations.

__eq__(other)
__ne__(other)

The two instance methods __eq__ and __ne__ check for equality respectively inequality of two views.

Ordinarily, both functions are called by way of the == and != operators.

name()
rename(new_name, force=False)

Instance method name returns the name of the view as string. The view name is not an ordinary attribute.

Instance method rename gives the view a new name. The new name must not be used by another view in the graph unless parameter force evaluates to ’True’ in which case the other view with that name is deleted before renaming the current one.

7.4.3.5. Value Handling for Attributes

For a full explanation of the attribute handling, see Section Attributes.

has_key(key)
__getitem__(key)
__setitem__(key, value)
__delitem__(key)
__iter__()
next()
keys()
items()
get_string_list(key)
set_string_list(key, value)
append_to_string_list(key, value)

These instance methods query and manipulate the attributes of a view. has_key checks whether attribute key is set, __getitem__ gets the stored value for attribute key, __setitem__ sets a new value for the attribute. __iter__ and next implement the iterator interface. keys returns a list of the names of all set attributes. items returns the list of all set pairs of keys (as strings) and values. get_string_list interprets the string attribute as list and returns the list. set_string_list stores a string representing the given string list. append_to_string_list appends the given string to the string list stored in the string attribute.

7.4.3.6. Membership in the View

Nodes and edges can be contained in views. The following methods influence and query the membership.

__contains__(element)
insert(element[, source, target])
add(element[, source, target])
insert_uniquely(element, source, target)
add_uniquely(element, source, target)
remove(element)

Instance method __contains__ checks whether the element passed as parameter (a node or an edge) is contained in the view. If you pass a ViewAttribute or a string as parameter, __contains__ checks if the attribute has a value (analogous to has_value). __contains__ is usable as in operator as well.

Caution

__contains__ simply returns ’False’ instead of raising an exception in case an attribute specified by name is not a valid view attribute.

Instance methods insert and add called with one parameter insert the element or set of elements they are supplied with into the view. You can call insert and add either with a node, an edge, a set of nodes, or a set of edges.

Note

Inserting an edge into a view also inserts the source and the target node of the edge if not already contained in the view.

The three parameter versions of insert and add are only usable for single edges that are not inserted in any view so far. A binding between the edge and its target and source nodes is established and the edge is inserted into the view.

Note

You have to supply the additional parameters source and target as named parameters.

Instance methods insert_uniquely and add_uniquely check if there is already an edge of the requested type between the two nodes source and target:

  • If not, the instance methods behave like insert.

  • If there is already an edge of the same type as parameter element, the parameter edge is freed implicitly and you must not use it further; the existing edge is returned.

See Listing Using the result of insert_uniquely for an example.

Using the result of insert_uniquely
...
   new_edge = some_graph.edge('Call')
   new_edge = some_view.insert_uniquely(new_edge, source=some_node, target=other_node)
   new_edge['Some.Attribute'] = 42
...

Caution

When creating a new edge, you have to insert it using the three parameter version for the first time to establish the target and source nodes of the edge.

Instance method remove removes the element or set of elements from the view. If the parameter is a node or a set of nodes, the attached edges are implicitly removed as well.

Caution

If you remove an element from the last view it has been contained in, the element is automatically garbage collected immediately. So make sure you first add an element to a target view before removing it from a source view if you intend to transfer the element between two views.

Caution

Hierarchies are expressed by edges. The edges have to form a tree in order to make the hierarchy work properly with many analyzes within the Axivion Suite . So make sure that you do not create non-tree structures as result of your view manipulations if you plan to use a view as a hierarchy.

nodes([node_matcher])
edges([edge_matcher])

These methods return all nodes respectively edges contained in the view. If the matcher is supplied, only those nodes/edges are put into the sets that are matched by the matcher function.

Note

As nodes and edges return a full-fledged set of elements, it is a good idea to use xnodes and xedges when iterating because of memory management reasons.

7.4.3.7. Iterating the Elements Contained in a View

xnodes([node_matcher])
xedges([edge_matcher])

Instance method xnodes returns an iterator object for all nodes contained in the view. It is of type ViewNodeIter. Instance method xedges returns an iterator object for all edges contained in the view. It is of type ViewEdgeIter.

An example of how to use this functions is shown in Listing Iterate the nodes in the Code Facts view

Iterate the nodes in the Code Facts view
from bauhaus import rfg
my_graph = rfg.Graph(filename)
my_view = my_graph.view(rfg.code_facts_view_name())
for n in my_view.xnodes():
    #...perform your actions on the nodes

Note

This schema of iteration is more efficient than obtaining a set of nodes/edges in the view.

7.4.3.8. View Operations

Views can be intersected, united, differenced, and copied. Edges of a view can be subtracted from a second view. Edges can be hierarchically lifted to get a view in which transitive dependencies are interactively browsable. Additionally, analyses for cycles, dead code and architecture violations (reflexion analysis) can be applied. Maps to edges can be replaced in a view in order to get a view that is browsable by its architecture.

The result of all following instance methods is – if not stated differently – the resulting view.

The example code in Listing View operations example illustrates the usage of the view operation instance methods:

View operations example
#!/usr/bin/env rfgscript
# Copyright (C) 2025 Axivion GmbH
# Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
# https://www.qt.io

import sys

from bauhaus import rfg

if len(sys.argv) < 2:
    print("usage: %s <rfgfile>" % (sys.argv[0]))
    sys.exit(1)

g = rfg.Graph(sys.argv[1])

base_view = g.view(rfg.code_facts_view_name())
env_view = g.view(rfg.environment_view_name())
call_view = g.view(rfg.call_view_name())

base_view.cycles()
base_without_env = base_view.difference(env_view)

# we ignore the result of these operations
base_without_env.union(call_view)
# we rename the result view of the copy operation
call_view.copy().rename("This is a copy of the Call view")
call_view.intersection(env_view)

module_view = g.view(rfg.module_view_name())
module_view.lift_edges_totally(base_view)

g.save(sys.argv[1] + ".new.rfg")

Intersection, Union, Difference

intersection(other_view[, result_name])
union(other_view[, result_name])
difference(other_view[, result_name])

These instance methods take the view instance for which their are called and perform the operations indicated by the method names with regard to the parameter other_view.

If you pass parameter result_name, it is disambiguated and used as name of the result view. If parameter result_name is not passed, the result view is named according the following table. If the name is already used, a counter is attached that disambiguates the name.

operation

name of result

intersection

self.name() + “ intersected with “ + other_view.name()

union

self.name() + “ united with “ + other_view.name()

difference

self.name() + “ without “ + other_view.name()

Caution

Hierarchies are expressed by edges. The edges have to form a tree in order to make the hierarchy work properly with many analyzes within the Axivion Suite . So make sure that you do not create non-tree structures as result of your view manipulations if you plan to use a view as a hierarchy.

The resulting view is returned.

Copy

copy([result_name])

The copy instance method makes a copy of the view.

If you pass parameter result_name, it is disambiguated and used as name of the result view. If parameter result_name is not passed, the result view is named according the following table. If the name is already used, a counter is attached that disambiguates the name.

operation

name of result

copy

“copy of “ + self.name()

The resulting view is returned.

Subtract Edges

subtract_edges(to_be_subtracted[, result_name])

The subtract_edges instance method creates a new view that contains the nodes from the view and all edges that are not contained in the argument view to_be_subtracted.

If you pass parameter result_name, it is disambiguated and used as name of the result view. If parameter result_name is not passed, the result view is named according the following table.

operation

name of result

subtract_edges

“edges from “+to_be_subtracted.name()+” subtracted in “+self.name()

The resulting view is returned.

Lift Edges

lift_edges_totally(other_view[, result_name])

The lift_edges_totally instance method reads the view as a hierarchy and takes the other_view as source of edges to be lifted within that hierarchy.

If you pass parameter result_name, it is disambiguated and used as name of the result view. If parameter result_name is not passed, the result view is named according the following table. If the name is already used, a counter is attached that disambiguates the name.

operation

name of result

lift_edges_totally

“lifted totally in “+self.name() +” edges from “+other_view.name()

The resulting view is returned.

Cycles

cycles([result_name])

The cycles instance method creates a new view that contains the cycles of the nodes and edges contained in the view. Cycles may overlap, so the result is strictly speaking the view of the strongly connected components of the original view.

If you pass parameter result_name, it is disambiguated and used as name of the result view. If parameter result_name is not passed, the result view is named according the following table.

operation

name of result

cycles

“cycles in “+self.name()

The resulting view is returned.

Note

If you want to compute cycles on the level of modules, you have to first lift the dependencies from the base level to the modules in an intermediate view, remove all base nodes from that intermediate view, remove all hierarchy edges from it and compute cycles on that intermediate result.

Dead Code

dead_code(entries_view[, alive_result_name[, dead_result_name]])

The dead_code analysis creates to views containing the alive and dead routines of a system. The base view is the view you called dead_code for. The entry points into the alive set are the nodes contained in the entries_view.

The result views are named according the following table if you do not supply values for the arguments alive_result_name and dead_result_name (which are disambiguated anyway, so there will be no views overwritten by this analysis):

result name argument

default name of result

alive_result_name

“alive”

dead_result_name

“dead”

The result is a dict of views containing the keys “alive_view” and “dead_view”.

Reflexion: Architecture Analysis

reflexion(base_view, hierarchy_view, mapping_view
[, result_name
[, causing_elements_view_name
[, allow_dependencies_to_parents
[, root_edge_type_name
[, browsable_mapping_type
[, check_declaration_forwarding
[, declare_edge_type
]]]]]]])

If you pass parameter result_name, it is disambiguated and used as name of the result view. If parameter result_name is not passed, the result view is named according the following table.

operation

name of result

reflexion

“reflexion of “+base_view.name()+” “+hierarchy_view.name()+” architecture “+self.name()+” mapping “+mapping_view.name()

If you supply a name for causing_elements_view_name, a causing elements view is created. The default is to create no such view.

Parameter root_edge_type_name specifies the root type of the edge type hierarchy which is used for the architecture check. The default is ’None’ which is interpreted as Source_Dependency.

The result of instance method reflexion is a dictionary containing the result view (as value for the key “result_view”) and the causing elements view (as value for the key “causing_elements_view”; the value is ’None’ if the causing elements view is not generated).

The parameter browsable_mapping_type indicates what kind of browsable mapping should be generated. It should be 'None' (or the None value) for no browsable mapping at all, 'Minimal' (only endpoints of issues are included), or 'Full' (all mapped nodes are included). If parameter check_declaration_forwarding is True (the default), mapping targets for declarations are computed by considering the definition of the declaration and a subset of other declarations thereof (see Architecture Analysis). The edge type for describing the relation between declarations and their definitions is given by declare_edge_type (the default is 'Declare').

reflexion_analysis_result(
[include_divergences
[, include_absences
[, include_convergences
]]])

The result of the most recent reflexion analysis can be queried using the function reflexion_analysis_result. The parameters (available since versions 6.9.18 in the 6.9 series and 7.0.2 in the 7.0 series) control which of the three reflexion results are included in the report. By default, only divergences and absences are included.

The return value is a list of pairs of edges (reflexion_edge, causing_edge).

  • iff reflexion_edge is an absence edge, causing_edge is an edge from the architectural model that does not cover any edge from the source model

  • iff reflexion_edge is a divergence edge, causing_edge is an edge from the source model that is not covered by any edge from the architectural model

  • iff reflexion_edge is a convergence edge, causing_edge (better read as: “conforming edge”) is an edge from the source model that is covered by the convergence edge in the result view (which is a duplicate of an existing source dependency in the architectural model)

Replace Maps_To Edges

replace_maps_to_edges(hierarchy_view, mapping_view[, result_name])

If you pass parameter result_name, it is disambiguated and used as name of the result view. If parameter result_name is not passed, the result view is named according the following table.

operation

name of result

replace_maps_to_edges

“maps to replaced for mapping “+ mapping_view.name()+ “ in architecture “+ self.name()+ “ with respect to hierarchy “+ hierarchy_view.name()

Instance method replace_maps_to_edges unites the view it operates on (taken as architectural view), the mapping view and the hierarchy view and replaces all maps to edges in the union by hierarchic edges. Edges from the hierarchy view that interfere with the hierarchy (because the mapped entity is itself contained in the hierarchy) are removed from the result view. The result view contains a browsable architecture with attached concrete entities. Use lift_edges_totally in order to get a view that contains transitive relationships.

The resulting view is returned.

7.4.3.9. Querying the Neighborhood of Nodes and Edges

The instance methods that take an optional matcher argument use the argument as a matcher function that filters the edges taken into account when computing the respective resulting sets of nodes. Only Edges that are matched by the matcher are taken into account.

If you give a single element (i. e. a single node or edge) as first parameter, it is handled like it has been the only element in a set passed as parameter.

incomings(element[, edge_matcher])
outgoings(element[, edge_matcher])
attached(element[, edge_matcher])
connectings(element[, edge_matcher])

The instance method incomings returns the set of all edges whose target is contained in the node set.

The instance method outgoings returns the set of all edges whose source is contained in the node set.

If you pass a node or node set to the instance method attached, it returns the set of all edges whose sources or targets are contained in the node set. In case you pass an edge or edge set to the instance method attached, it returns the set of all source and target nodes of the edge respectively the edges.

The instance method connectings returns the set of all edges whose source and target are contained in the node set.

successors(element[, edge_matcher])
predecessors(element[, edge_matcher])
neighbors(element[, edge_matcher])

The instance method predecessors returns the set of all nodes connected to the argument node set by means of edges that have a target in the argument node set.

The instance method successors returns the set of all nodes connected to the argument node set by means of edges that have a source in the argument node set.

The instance method neighbors returns the set of all nodes connected to the argument node set by means of edges that have a source or target in the argument node set.

Note

The edges forming a hierarchy are directed from the part to the container. Therefore, the direct children of a node X can be obtained by applying predecessors to X.

sources(edges[, node_matcher])
targets(edges[, node_matcher])
source(edge[, node_matcher])
target(edge[, node_matcher])

The instance method sources returns the set of all source nodes of the argument edge set.

The instance method targets returns the set of all target nodes of the argument edge set.

The instance method source returns the source node of the argument edge.

The instance method target returns the target node of the argument edge.

If parameter node_matcher is used, only nodes matching the node matcher are returned. In case of source and target, ’None’ is returned if the source/target node does not match the node matcher.