ICER Tool Application: Software Refinement

Modern software development is a complicated process especially when a software system becomes large and complicated. Software developers must apply software refinement in order to proceed from a high-level abstract model to a final executable software system by adding more details over time. Class diagrams are important because they represent the static structure of a software system, and therefore we design a UML profile extending the UML metamodel to support class diagram refinement.

The main idea of this research is to help developers check whether two class diagrams at different levels of abstraction have any semantic inconsistencies that could have been introduced during the refinement process. How to represent the inconsistency between two class diagrams is important in supporting such a model refinement. Here we argue that the refinement rules which can find the inconsistencies between two classes at different levels of abstraction are closely related to the development context. There are no fixed rules; instead the developers can decide which rules are used. To satisfy this flexibility, we propose the UML profile mechanism be applied to represent the consistency rules. The most important advantage of using the UML profile mechanism is that developers can design a profile to reflect their consistency rules depending on their development context. In the following we show how to apply the profile mechanism to define a refinement rule and how our tool, ICER, finds an error.

The UML profile defines some stereotypes that software developers can use to tag classes and relations in the class diagrams at the different levels of refinement. We use OCL constraints to represent refinement rules that elements at the two levels of refinement must adhere to, and use the model instantiation checking feature of our tool to validate a UML model containing the two levels of class diagrams against the UML profile containing these OCL constraints.

The notation we use to separate class diagrams at two different levels within the same UML model is similar to the way we separate specification and instance diagrams within a single UML model. We use packages containing class diagrams at each level and connecting the package containing the higher- level class diagram with the package containing the refined class diagram with a dependency. These packages are located within the instance diagram package. The figure below shows this relation. The package labeled Level 2 is the higher-level class diagram while the package labeled Level 3 is the refined class diagram. As with instance and specification diagram packages, there can be multiple packages within the instance diagram packages as long as there is only one dependency relation between the packages.

Package notation for class diagram refinement
notation for refinement

In the UML profile for software refinement we define a number of stereotypes. The stereotype Refine_Class extending the metaclass Class is used to tag classes that take part in a refinement relation. A stereotype that has the prefix Refined_ represents an element to be refined, i.e. its instance belongs to a higher-level model. A stereotype with the prefix Refining_ represents an element that is used to refine an element at the higher-level model.

For the Refine_Class stereotype we define a tag named mapping_name that is used to represent a refinement relation between two classes during class diagram refinement. If two classes, one at each level of refinement, have the same value for mapping_name, then these two classes are involved in some refinement relation. Each of the other stereotypes have a tag named refinement_value, which is used to represent elements that are involved in a refinement relation. All Refined_ and Refining_ stereotypes with the same value for refinement_value are involved in some refinement relation. In addition to this, each Refining_ stereotype also has a tag named mapping_order, which is an integer value that represents the order in which the refining element appears in the relation that is refined in the refined class diagram. The figure below shows part of the UML profile supporting class diagram refinement.

Part of UML profile supporting class diagram refinement
refinement UML profile

In the preceding figure two stereotypes, Refined_Gen and Refining_Gen, are defined. As an example we will present a refinement rule involving generalizations, shown in the figure below. The rule specifies that a generalization between two classes in a higher-level class diagram can be refined into a series of generalizations involving intermediate helper classes in the refined class diagram. The bottom part of the figure illustrates this.

One refinement rule for generalizations
generalization refinement

The following OCL constraint represents this refinement rule:

context Refined_Gen
inv: let matchingGen : Set(Generalization) =
  Generalization.allInstances()->select(g| g.oclIsKindOf(Refining_Gen) and
    g.oclAsType(Refining_Gen).refinement_value = self.refinement_value)
  self.parent.oclIsKindOf(Refine_Class) and
  self.child.oclIsKindOf(Refine_Class) and

  -- no duplicate mapping_order values
  matchingGen->forAll(a,b | a.oclAsType(Refining_Gen).mapping_order =
    b.oclAsType(Refining_Gen).mapping_order implies a = b)

  -- mapping_order values between 1 and size of set of
  -- Refining_ relations with same refinement_value
  and matchingGen->forAll(a|
    a.oclAsType(Refining_Gen).mapping_order >= 1 and
    a.oclAsType(Refining_Gen).mapping_order <= matchingGen->size())

  -- refining end classes match refined end classes
  -- relation sequence should be numbered from child to parent
  and matchingGen->exists (a,b | a<>b and
    a.oclAsType(Refining_Gen).mapping_order = 1 and
    a.child.oclIsKindOf(Refine_Class) and
    a.child.oclAsType(Refine_Class).mapping_name =
    self.child.oclAsType(Refine_Class).mapping_name and
    b.oclAsType(Refining_Gen).mapping_order = matchingGen->size() and
    b.parent.oclIsKindOf(Refine_Class) and
    b.parent.oclAsType(Refine_Class).mapping_name =

  -- consecutive relations connect the same classes
  -- relation sequence should be numbered from child to parent
  and matchingGen->forAll (c,d | (c<>d and
    c.oclAsType(Refining_Gen).mapping_order + 1 =
    d.oclAsType(Refining_Gen).mapping_order) implies
    (c.parent = d.child))

Let us take a look at a UML model using this refinement rule (download the model, download the XML file). The figure below shows the high-level class diagram. Notice that there can be multiple sets of classes and relations that are tagged with the appropriate stereotypes. However, each refined relation (association or generalization in this case) have different values for refinement_value so there is no ambiguity as to which refining elements in the refined class diagram correspond to which refined elements in the higher-level class diagram. The mapping_name tagged value for each <<Refine_Class>> class is not shown because they are in documentation tags (see Notation). For our example, the class A has the value "src1" while the class C has the value "src3" for their mapping_name.

High-level class diagram example
generalization refinement example

The figure below shows part of the refined class diagram for the generalization between classes A and C in the diagram before it. The names of the two end classes have been changed during the refinement process. However, the values of refinement_value for X and TheChild are still "src1" and "src3" respectively. A helper class G1 has been added in between the original two classes. However, there is an error because now TheChild is no longer a descendant of X as it was in the abstract model.

Incorrect refined class diagram
incorrect generalization refinement

Before proceeding let us first take a look at how to validate software refinement with the tool.

When ICER reads in the UML model containing the UML profile for refinement and the class diagrams at the different levels, the OCL constraint for the generalization tagged with <<Refined_Gen>> in the more abstract class diagram will be checked. Because there is an error in the refined class diagram, the tool will find the error, shown in the diagram below.

Tool output showing refinement rule violation
tool output indicating error in refinement

From the output of the tool we know that the refinement is not correct. Right now we cannot pinpoint exactly which part in the OCL constraint representing the refinement rule was violated, but if there are multiple refining relations in the UML model we can at least narrow it down to a particular set of refining relations. The error in this example can be fixed by reversing the generaliztion between X and G1:

Fixed refined class diagram
fixed generalization refinement

Now when we export the UML model and rerun ICER on the exported XMI file we get no errors from the resulting execution as shown below.

Tool output with no errors
tool output with no errors