2.16.  Class properties

[Warning]Text not verified for kermeta 2

A property of a (meta)class can be expressed in three ways : as a reference, an attribute, or a derived property. In Kermeta, each kind of these properties has a specific behavior and a dedicated syntax, which is attribute (for attribute), reference (for reference), property (for derived property)

References and attributes can have opposite properties. This last concept is explained in the following subsection.

Unlike UML, there is no concept of visibility in Kermeta, so every property is visible

2.16.1.  Attributes (attribute) , references (reference)

We introduce here the 2 first cases, which are relationships between two concrete entities.

  • attribute : an attribute defines a composition (e.g the black diamond) between two entities. The diamond-ed association end is navigable by definition

class A { attribute x : set X[0..*] } 
class X {}
[Note]Note

Composition notion also relates to containment notion. So, there are some restriction about valid model. For example, in an association, only one end can be an attribute, otherwise this means that we have an association with a diamond on both end and we cannot say who is the container of the other.

  • reference : a reference defines an association between two entities.

class A { reference x : set X[0..*] } 
class X {}

2.16.2. properties modifiers

Attributes, references and properties underlying collections may eventually be specialized. By default, they are represented by an OrderedSet. If you wish to be more precise and for exemple allow to have several items with the same value in your collection, you can use the collection modifiers as defined in Section 2.15, “ Collections ”

For example :

class A {
	attribute x1 : seq String[0..*]  // allows duplicates, ordered
	attribute x2 : set String[0..*]  // doesn't allow duplicates, not ordered        
	attribute x3 : bag String[0..*]  // allows duplicates, not ordered               
	attribute x4 : oset String[0..*] // (default if no modifier) doesn't allow duplicates, ordered        
}        

2.16.3. Association using opposite properties

The opposite [property] of a property defines an association (bidirectional link) between two entities. An opposite is expressed by a sharp #. In the following example, a is the opposite property of the entity of type B, and b is mutually the opposite property of the entity of type A.

[Caution]Caution

A property whose type is a DataType (i.e String, Boolean, Integer) cannot have an opposite!

Example 1 : definition of an attribute, a reference, and an opposite property

This means that a can be accessed from b. Subsequently, if you modify the property b of an instance of A, it will mutually update its opposite property, i.e the property a of the instance of B contained in property b. You can make a test with the following example of code.

Example 2 : navigability of opposite properties

var a1 : A init A.new
a1.name := "a1"
var b1 : B init C.new
a1.b := b1
stdio.writeln("b1 opposite : " + b1.a.name) // This prints "a1"!

The following paragraph shows a set of examples of attributes and references.

Attributes and references

Figure 2.5. Attributes and references


Example 3 : a set of attributes and references, with multiplicities and opposites.

TODO : add an example of code using those classes !! See https://gforge.inria.fr/plugins/scmcvs/cvsweb.php/integration_projects/other/org.openembedd.formations/Kermeta/Basics/docs/FR_formation_Kermeta_1er_niveau.odp?cvsroot=openembedd for such examples (p. 21).

package root;
class A1 {
    attribute b : B1[0..*] 
}
class B1 {}
			
class A2 {}
class B2 {
    reference a : A2 
}
			
class A3 {
    reference b : B3#a
}
class B3 {
    reference a : A3#b
}
			
class A4 {
    reference a : A4[0..*]
}
class A5 {
    attribute ab : A5[0..*]#aa
    reference aa : A5#ab
}
class A6 {
    attribute b : B6#a
}
class B6 {
    reference a : A6[1..1]#b
}
class A7 {
    attribute b : B7[0..*]#a
}
class B7 {
    reference a : A7#b 
}
class A8 {
    attribute b : B8[1..1]#a
}
class B8 {
    reference a : A8#b
}
class A9 {}
class B9 {
    reference a : A9[1..1]
}
[Note]Note

For every case where the upper bound of a property is upper to 1, the type of the property is OrderedSet . The reader will refer to Section 2.15, “ Collections ” (except the bag type) to have the available types for a [m..n](n>1) multiplicity property.

2.16.4.  Derived properties (property)

In a class definition, a derived property is a property that is derived or calculated, i.e it contains a body, like operations. Usually, such properties are calculated from other properties available from its owning class definition. In practice, you can define the code that you want inside a derived property.

In other words it does not reference to a concrete entity: it is calculated, through the accessor operations getter and setter.

The special parameter value is used in the setter to get the value passed when calling the setter.

Let's take the following class definitions :

// readonly property : it has no setter
class A 
{
    attribute period : Real
    property readonly frequency : Real // property : keyword for derived property
        getter is do
            result := 1/period
        end
}
// modifiable property :
class B 
{
    attribute period : Real
    property frequency : Real
        getter is do
            result result := 1/period
        end
        setter is do
            period := 1/value
        end
}

// a typical use would be (with aB an instance of class B) 
var freq : Real
fred := aB.frequency // to use the getter
aB.frequency := freq + 1 // to use the setter, the period is also updated in the process 
[Note]Note

To understand to role of the value keyword, you can imagine that in this sample the setter syntax is a shortcut syntax of : setter( value : Real) is do ... (even if actually this syntax isn't supported).

[Warning]Warning

Since derived properties aims to behave like attributes or references, if the multiplicity is greater than 1, it doesn't make sense to define a setter. This is because it means reassigning the internal collection which is in fact calculated.

Properties are accessed or modified as classical properties. See next subsection for examples.

2.16.5. How to access and control the properties in Kermeta

Example 1 : let's take the example with A6 and B6 :

class A6 {
     attribute b : B6[0..*]#a
}

class B6 {
    reference a : A6#b 
}

Access

Kermeta expression

Get the attribute of an instance

var a6 : A6 init A6.new
var b6 : OrderedSet<B6>
// get the b attribute
// Note that as the attribute as a multiplicity >1 it is an OrderedSet 
b6 := a6.b

Add an element to a property with multiplicity [m..n], n>1

var a6 : A6 init A6.new
var b6 : B6 init B6.new
// add b6 to the attribute b of A. 
// Note : you don’t have to initialize b! done through A6.new
a6.b.add(b6)

Remove an element from a property

// OrderedSet owns a method that removes an element given its
     // index in the set. For unordered sets, use "remove" method
a6.b.removeAt(0)
// Also valid : a6.b.remove(b6)

Get the opposite of a property

var a6 : A6 init A6.new
var b6 : B6 init B6.new
a6.b.add(b6)
// this assertion is true. Moreover, any instance in a6.b will
// have a6 as their opposite since b is a collection
assert(b6.a == a6)

Get the container of a property

var a6 : A6 init A6.new
var b6 : B6 init B6.new
// add ab6 to the attribute "b"
a6.b.add(b6)
var a6c : A6 init b6.container()
// this assertion is true
assert(a6c.equals(a6))

It is not different with references that have a [m..1] (m=0 or m=1) multiplicity:

Example 2 : the A5 B5 case

class A5 {
    attribute b : B5#a // no multiplicity means [0..1]
}
class B5 {
    reference a : A5[1..1]#b
}

Access

Kermeta expression

Get the attribute of an instance

var a5 : A5 init A5.new
var b5 : B5
// get the b attribute
b5 := a5.b

Set a property (with multiplicity [m..1], m≤1)

var a5 : A5 init A5.new
var b5 : B5 init B5.new
// set b5 to the attribute b of A.
a5.b := b5

Unset a property

a5.b := void

Get the opposite of a property

var a5 : A5 init A5.new
var b5 : B5 init B5.new
a5.b := b5
// this assertion is true.
assert(b5.a == a5)

Get the container of a property

var a5 : A5 init A5.new
var b5 : B5 init B5.new
// add b5 to the attribute "b"
a5.b := b5
var a5c : A5 init b5.container()
// this assertion is true
assert(a5c.equals(a5))
[Note]Note

Be careful with attributes or references ref with multiplicity greater than 1, they are automatically initialized with a reflective collection (ie. a collection that is aware of an eventual opposite). So, you cannot assign them using := If you wish to change completely its content with the one of another collection, you must use the clear and addAll operations.

[Note]Note

For the same reason, reference refA : OrderedSet<String> is different from reference refB : String[0..*] . If both can be navigated the same way (using add, each or any operation available on Collection), refA needs to be created before use with refA := OrderedSet<String>.new . refA is a reference to a colection whereas refB is a reference to String with a multiplicity greater than 1 (which is internally represented as a ReflectiveCollection<String>)

2.16.6. Assignment behavior for attribute (and reference)

Attribute and reference have one main behavior difference.

Attribute has a notion of containment that reference hasn't.

This has some implication on the behavior of the assignment because an attribute cannot be owned by more than one object at a time.

There is an exception for attributes which type is a primitive type (String, Integer, Boolean, Real, Char) : since those types inherit from ValueType , they are not concerned by the composition, opposite concepts. In this case, the assignment doesn't impact any value but the assigned one.

Example 1 : Assignment behavior for attribute

class A { attribute c1 : C }
class B { attribute c2 : C }
class C { }
aA.c1 := C.new
aB.c2 := aA.c1     // now aA.c1 == void !!!

Example 2 : Assignment behavior for reference

class A { reference c1 : C }
class B { reference c2 : C }
class C { }

aA.c1 := C.new
aB.c2 := aA.c1     // aB.c2 == aA.c1 and aA.c1 keeps its value !!!

Example 3 : Assignment behavior for attribute which type is String

class A { reference c1 : String }class B { reference c2 : String }aA.c1 := "Robert"aB.c2 := aA.c1 // aB.c2 == aA.c1 == "Robert"
[Note]Note

The assignment into a variable or a reference is not a problem because it doesn't change the owner of the assigned object.