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
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 | |
---|---|
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 {}
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 }
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 | |
---|---|
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.
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 | |
---|---|
For every case where the upper bound of a property is upper to 1,
the type of the property is
|
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 | |
---|---|
To understand to role of the
|
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.
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 | |
---|---|
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
|
Note | |
---|---|
For the same reason,
|
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 | |
---|---|
The assignment into a
|