Chapter 4. Manipulate instance-models with Kermeta

Loading a first Kermeta program can be achieved by just copying the code provided in the following sections, 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 of the loaded model (there is usually only have one root class). So, the procedure of creation of a resource that will handle EMF models is the following (code example 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). All other instances can then be accessed by navigating the root class(es) properties.

4.1.2. A first Kermeta program using persistence

As a reminder, here is the common skeleton of a Kermeta program, inside which can must be added 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

The following code sample load a previously created FSM model:

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+ " -- " +t.target.name ) }
end

4.3. Modify and save an EMF model with Kermeta

At this stage, it should be interesting to be able to modify a previously loaded model using Kermeta before saving 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. For this purpose, the following code can be added 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()

It is also possible to save the modified model in a new file instead of overwriting the initial one by using the saveWithNewURI() method. To this end, just replace the last line of above code (resource.save()) by the following one:

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

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

Saving a programmatically generated model requires to use a new specific instruction that addq the created Fsm root class to the destination resource. The following code chunk creates a simple EMF model with 2 states (named “foo”, and “bar”), and 2 transitions. Saving it then consists in adding the root class (i.e. the model object) stored in the variable fsm2 into the resource instances.

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()

This program should return the following FSM model (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

    The following short code sample provides a comprehensive code template (replace the <words>) for model loading. Note here that the term model object is appropriate (better than root class!): loading a model consists in getting the root class, from which, thanks to the containment property (see section 1.2, p.2), all the contained instances can be accessed.

    @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
    }