Chapter 4. Manipulate EMF models with Kermeta

To load your first kermeta program, you will just have to copy the code provided in the following chapters, following carefully the suggested instructions. Readers who want to directly load their own models should directly go to the section 4.5, p.15 and copy the given template.

4.1. Preparing a Kermeta program

4.1.1. The persistence library

The persistence library is inspired from the resource manager of EMF models. There is a repository (called EMFRepository), that is aimed at containing a set of resources (the EMFResources). Each resource contains a reference called instances, that contains all the root classes (usually you only have one root class) of the loaded model. So, the procedure of creation of a resource that will handle your EMF models is the following (code exammple is provided in next sections) :

  1. instanciate an EMFRepository

  2. create a new EMF resource in this repository

  3. load this resource

  4. get the instances, i.e the root class(es). When you have them, you can access all the other instances through accessing the root class(es) properties.

4.1.2. Your first Kermeta program using persistence

As a reminder, here is the common skeleton of a kermeta program, inside which you will add any library that is necessary to load and save the fsm samples.

@mainClass "fsm_package::Main"
@mainOperation "main"

package fsm_package;

require kermeta
require "../metamodels/fsm.ecore"
using kermeta::persistence // <- used to load and save EMF models.
using kermeta::standard

class Main
{
     operation main() : Void is do 
         // TODO: implement 'main' operation
     end
}

4.2. Load an EMF model with Kermeta

Here, we will load one of the Fsm models that we previously created:

operation main() : Void is do 
 // Input fsm
 var fsm1 : fsm::Fsm
 // Create the repository, then the resource
 var repository : EMFRepository init EMFRepository.new
 var resource : EMFResource 
 resource ?= repository.createResource("../models/Fsm_dyn_sample1.xmi", "../metamodels/fsm.ecore")
 resource.load

 // Load the fsm (we get the instance) 
 fsm1 ?= resource.instances.one
 // Check that the fsm was correctly loaded
 fsm1.state.each { s | stdio.writeln("-> "+s.name) }
 fsm1.transition.each { t | stdio.writeln( t.source.name " -- " +s.target.name ) }
end

4.3. Modify and save an EMF model with Kermeta

Now, you want to modify your model using Kermeta, and then to save it. The procedure is very simple : do your manipulation as if your loaded fsm model is a Kermeta model (which is, in effect, the case!), and then, simply call a save method on the handling resource. Thus, you can add the following code at the end of the main operation defined in the above section :

var newstate : fsm::State init fsm::State.new
newstate.name := "s_new"fsm1.state.add(newstate)
// save fsm1 
resource.save()

You can also save your model in a new file instead of overwriting the initial one by using save():(we just replace the last line of above code (resource.save()) into this one:

resource.saveWithNewURI("../models/modified_dyn_sample1.xmi")

4.4. Create a model in Kermeta, and save it as an EMF model

For this purpose, you will only have to do one specific instruction, that is, add the Fsm root class created from scratch to the destination resource. The following code chunk creates a simple EMF model with 2 states (named “foo”, and “bar”), and 2 transitions. Then saving it consists on adding the root class (i.e the model object) stored in the variable fsm2 in the instances of the resource .

var another_resource : EMFResource
another_resource ?= repository.createResource(
       "../models/Fsm_scratch_sample.xmi",
       "../metamodels/fsm.ecore")
var fsm2 : fsm::Fsm init fsm::Fsm.new
var s0 : fsm::State init fsm::State.new
var s1 : fsm::State init fsm::State.new
var t01 : fsm::Transition init fsm::Transition.new
var t11 : fsm::Transition init fsm::Transition.new
s0.name := "foo"
s1.name := "bar"
t01.source := s0
t01.target := s1
t11.source := s1
t11.target := s1
fsm2.state.add(s0)
fsm2.state.add(s1)
fsm2.transition.add(t01)
fsm2.transition.add(t11)
// save the from-scratch model!
another_resource.instances.add(fsm2)
another_resource.save()

You should have the following result (viewed with the reflexive editor):

Fsm_scratch_sample view

Figure 4.1. Fsm_scratch_sample view

    4.5. A template for a complete kermeta program

    To load your own models, you can simply fill in the short template code (replace the <words>) given below. Note that we can see that the term model object is appropriate, (better than root class!) : loading a model consists on getting the root class, from which, thanks to the containment property (see section 1.2, p.2), we access all the contained instances.

    @mainClass "fsm_package::Main"
    @mainOperation "main"
    package fsm_package;
    require kermeta 
    require "<relative_path_of_the_metamodel>" // NOTE : same as param of createResource
    
    using kermeta::persistence
    using kermeta::standard
    class Main
    {
     operation main() : Void is do 
          // Variable for your input EMF model
          var <my_model_object> : <type_of_my_model>
          // Create the repository, then the resource
          var <my_rep> : EMFRepository init EMFRepository.new 
          var <my_resource> : EMFResource 
          <my_resource> ?= repository.createResource(
          "<relative_path_of_my_model_to_load>",
          "<relative_path_of_the_metamodel>")
          <my_resource>.load
          // Load the emf model - get the root class 
          <my_model_object> ?= resource.instances.one
         // You can now browse your model through its attributes/references 
          <my_model_object>.<an_attribute_of_it>.each { o | 
             stdio.writeln("-> "+o.toString) } )
         // Save your model in another file
          <my_resource>.saveWithNewUri("<relative_path_of_a_file_where_to_save_model>")
     end
    }