Text not verified for kermeta 2 | |
---|---|
Since version 0.4.1, it is possible to write Kermeta code using a simplified Aspect Oriented approach.
Technically, you can declare classes as "aspects" that will contribute features (attributes, references, properties, operations, constraints) to an existing classes. In such situation, the definition of two classes that have the same qualified name will be merged into a single class at run time.
This is a great help when you want to separate the concerns into several files. For example, you may have one file containing the structural part of your metamodel, one file containing the constraints for a given purpose and another file containing the operation and special extension to the metamodel for an interpreter.
As a general guideline, this allow to reopen existing structure in order to add new element. It is not possible to remove or change an existing information. For example, you cannot trnasform an abstract class into a concrete class.
Obviously, the merge will be successful only if there is no conflict between all the declared features.
The merge is driven by the qualified name of the element to merge. Two classes will be merged if they have exactly the same qualified name (packages names + class name)
The merge is allowed only if you add the following keyword:
This keyword is placed on a class, it indicates that this class is an aspect of another one. This allows to complement a class with the features of the aspect class.
In this sample, writing:
package pack; // this is the first aspect of class A aspect class A { attribute decorations : Decoration[0..*] } // this aspect also declares a new class, visible only when requiring this aspect class Decoration{ attribute val : kermeta::standard::String }
package pack; // this is the base class class A { attribute name : kermeta::standard::String // operation in this context have access only to the base's features operation getName() : kermeta::standard::String is do result := name end }
package pack; // is this context we have access to all the features of class A : from base, aspect1 and aspect2 aspect class A { attribute id : kermeta::standard::Integer operation getFullSpecification() : kermeta::standard::String is do result := name result := "(" + id.toString + ")" decorations.each{ decoration | result := result + "<" + decoration.val+ ">" } end }
KermetaProject "aspect_sample" groupId = "my.group" defaultMainClass = "sample::AspectExample" defaultMainOperation = "main" sources = { require "${project.baseUri}/base.kmt" require "${project.baseUri}/aspect1.kmt" require "${project.baseUri}/aspect2.kmt" } dependencies = { //default dependency to kermeta framework (try first in eclipse plugin, then look into maven repository) dependency "library.core" ="platform:/plugin/org.kermeta.language.library.core", "mvn:org.kermeta.language/language.library.core/LATEST" }
New in Kermeta 2 | |
---|---|
In kermeta 1, the require statements were at the file level, now they apply at the project level. This means that the contibution of an aspect will be visible in all the project. It is still possible to manage the visibility of aspects using project dependencies. It is often useful to separate some set of files into projects. |
Tip | |
---|---|
In the previous example we aspectize a *.kmt, we could choose to aspectize the corresponding *.ecore file. In fact, the both formats are available: *.kmt or *.ecore. |
from the point of view of the project, the 3 kmt files are equivalent to write a single kmt file containing :
package pack; class A { attribute name : kermeta::standard::String attribute decorations : Decoration[0..*] attribute id : kermeta::standard::Integer // operation in this context have access only to the base's features operation getName() : kermeta::standard::String is do result := name end operation getFullSpecification() : kermeta::standard::String is do result := name result := "(" + id.toString + ")" decorations.each{ decoration | result := result + "<" + decoration.val+ ">" } end } class Decoration{ attribute val : kermeta::standard::String }
This sample shows that if the signature are strictly equivalent, then you can redefine the same structural feature (attribute, reference) in several aspect class.
In addition, if an operation can be extended to addpre or post conditions to a given operation.
New in Kermeta 2 | |
---|---|
In kermeta 1, and abstract operation was used to define an
operation with no body as a support
for operation extensions. In
kermeta 2 we suppose that if the developper has defined the
operation abstract then his intents is that the operation must be
overriden via a sub class. So
now, the abstract keyword cannot be
used for that. We use the
|
Currently, (v1.0.0) the derived properties cannot be redefined in several aspect classes.
aspect class A { //the feature is equivalent to the one define in the base class, so this is legal attribute name : kermeta::standard::String // the operation has the same signature and has no body (ie. void) operation getQualifiedName() : kermeta::standard::String post resultNotVoid is not result == Void is void }
package pack; class A { attribute name : kermeta::standard::String operation getQualifiedName() : kermeta::standard::String is do result := name end }
In ecore, it is possible to define some operation with body. this
is acheived either via the
generated NOT
comment in the generated java file or via a specific annotation in
the ecore file (Please refer
to EMF documentation and book for
details on that). By default, kermeta will reuse these
operations
and allows to call this code. However, in some situations, you may
want to overload
this code by your own in kermeta. This can be done
as follow:
package pack;
require kermeta
aspect class A {
@overloadable "true"
operation getQualifiedName() : kermeta::standard::String
post notVoid is not result == Void
is abstract
}
package pack;
require kermeta
aspect class A {
@overloadable "true"
operation getQualifiedName() : kermeta::standard::String is do
raise kermeta::exceptions::NotImplementedException.new
end
}
class A { attribute name : kermeta::standard::String operation getQualifiedName() : kermeta::standard::String is do result := name end }
In this sample, the first declaration of the operation is adds a post condition to the operation. As it is abstract, it isn't in conflict with the other declarations.
The second declaration, has a body. It implements a kind of default behavior for the operation that will be used if no other body is declared for this operation. this is useful when converting ecore models into Kermeta for example.
The last definition is the one that will be used in this context.
If you define some inheritance in one of your aspects, all the aspects that require this aspect will have access to it. You don't need to redefine it. However, if it helps to clarify the code to write it again, it will behave the same way.
In Kermeta textual syntax, the keyword aspect must be placed before the keyword abstract.
Example :
package pack; require kermeta aspect abstract class A { // add something }
A special case of the use of AOP in Kermeta is having a ClassDefinition A defined in 2 separately modeling units (*.kmt) with the keyword aspect. But the two modeling units does not require another kmt or ecore file containing the base definition of A. Now if there is a new Modeling Unit that requires the two previous one the available definition of A is the composition of the 2 defined aspects.