Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

This tutorial will be all about the Eclipse Modeling Framework, one of the core technologies in the Eclipse universe. You will learn what metamodels are and how to create them, how to generate an editor for instances of your metamodels, and how to load and save such instances. We will of course continue our example from the last tutorial: the The metamodel you will create will be one for models that specify Turing Machines. To learn about using EMF models programmatically, you will also implement a head controller that executes a Turing Machine given to it in terms of a Turing Machine model.

...

You may want to download the slides of the presentation explaining the basic concepts you will explore in this tutorial.

Table of Contents

 

Preliminaries

...

...

...

...

...

 

Preliminaries

The Eclipse Modeling Framework (EMF) belongs to the most important technologies in the Eclipse world. One of the more The Eclipse Modeling Framework (EMF) belongs to the most important technologies in the Eclipse world. One of the more recent indicators of this is its being used to describe the very foundation of Eclipse 4 (or e4, which of course sounds way cooler) applications: the application model. We might come back to the application model in the project phase of our practical. Another indication is that EMF has come to be the foundation of most other modeling-related Eclipse projects. Thus, there is important stuff to be learned in this tutorial!

...

Your Eclipse installation already has everything we need for this tutorial. In fact, that's one reason why we suggested you install the Eclipse Modeling Tools. You If part of a greater project, you will again be working on your branch of our tutorials Git repository.

Finding Documentation

For an introduction to EMF, here's a few suggestions to get you started:

  • The Wikipedia article on metamodeling may be i
  • The Eclipse online help system contains a section on EMF, complete with an introduction and tutorials.
  • There is a book on EMF, which is a great resource. The library may have copies available. We also have at least one copy at our office, so feel free to drop in and read it. (We also have tea and a sofa, so there's no lack of proper reading atmosphere... (wink) )
  • Among many other helpful tutorials, Lars Vogel, an active Eclipse developer, has also written a tutorial on EMF.

Creating a Metamodel

As we have already seen, everything in EMF begins with the metamodel. Metamodels can be specified in different formats: XSD (XML Schema Definition), UML, Ecore models,... In this tutorial, we will be using Ecore models to specify our metamodels. Take a moment to think about this: we're creating an Ecore model by drawing a diagram, and this model will then be used as the metamodel for our Turing Machine models. So, let's start by creating an Ecore diagram.

...

  • turingmachine.ecore contains the information about the data structures in your Turing Machine models. In effect, it is your metamodel in Ecore format.
  • turingmachine.ecorediag is the diagram you're editing and contains things like coordinates of the different data structures, and bend points of the relations between them – in short, everything the graphical editor needs to know to display the diagram.

...

Otherwise, you should perform this tutorial on your local workspace.

Note
titleEclipse installation

If you're not working with a copy of the Eclipse Modeling Tools yet, we recommend that you download the Eclipse Modeling Tools now. You might also want to check out the tutorial about Eclipse Plug-ins and Extension Points.

Additionally, install the EcoreViz from the Ecore Model Visualization category from the OpenKieler update site: http://rtsys.informatik.uni-kiel.de/~kieler/updatesite/nightly-openkieler/.

Image Added

Finding Documentation

For an introduction to EMF, here's a few suggestions to get you started:

  • The Wikipedia article on metamodeling may be of interest.
  • The Eclipse online help system contains a section on EMF, complete with an introduction and tutorials.
  • There is a book on EMF, which is a great resource. The library may have copies available. We also have at least one copy at our office, so feel free to drop in and read it. (We also have tea and a sofa, so there's no lack of proper reading atmosphere... (wink) )
  • Among many other helpful tutorials, Lars Vogel, an active Eclipse developer, has also written a tutorial on EMF.

Creating a Metamodel

As we have already seen, everything in EMF begins with the metamodel. Metamodels can be specified in different formats: XSD (XML Schema Definition), UML, Ecore models,... In this tutorial, we will be using Ecore models to specify our metamodels. Take a moment to think about this: we're creating an Ecore model by drawing a diagram, and this model will then be used as the metamodel for our Turing Machine models. So, let's start by creating an Ecore diagram.

  1. Create a new Empty EMF Project named de.cau.cs.rtprak.<login>.turingmodel. Remember to create the project in your Git repository if you're working with one. Once you click the Finish button, the Empty EMF Project wizard creates a new plug-in project for you, complete with a src folder for Java source files, the MANIFEST.MF file we have encountered before, and, most importantly, a models folder that you will store your modeling files in. If you open the manifest file in the Plugin Manifest Editor, you will see that the wizard already added a dependency to org.eclipse.emf.ecore, which all EMF projects depend on.
  2. Create a new Ecore Model in the models folder by right-clicking the folder and clicking New -> Other..., and then selecting Ecore Model from the Eclipse Modeling Framework category. 
  3. In the Ecore Model Wizard name your model turingmachine.ecore. The wizard will then create a new Ecore model file for you. Open the model file and select the unnamed package in it.
  4. You will need the Properties view to edit your model properly. This view shows detailed information about the currently selected model element and lets you edit them. It also shows general information about the model if no specific element it selected. Summon the Properties view now by right-clicking into your diagram and selecting Show Properties View.
  5. Now that the Properties view is visible, switch to its Model tab and set the following properties:
    • Name: Model elements are grouped into packages, and this is the package name. Set to turingmachine.
    • Ns Prefix: Namespace prefix that will be used in the XML representation of your models later on. Use something short, e.g. turing.
    • Ns URI: While the package name need not be unique, namespace URI's are used to uniquely identify stuff. The usual convention is to use a name following the format http://project_name_part/packagename. Thus, set this to something like http://de.cau.cs.rtprak.login<login>/turingmachine.

Modeling Your Turing Machines

...

  • EClass – Create one for every item that you want to be in your model. Make sure that you have exactly one root element, that is, an element that represents your Turing Machine (perhaps TuringMachine would actually be a good name for it...) and provides access to other elements.
  • EAttribute – Add attributes to classes to give them properties, e.g. a name attribute for states in state machines. The most important property of attributes is their type, which you can configure in the Properties view.
  • EEnum – Create enumerations to define simple enumeration types that you can then use as the type of attributes. Each item of the enumeration is an EEnumLiteral.
  • Inheritance Relations – Use these as you would in UML class diagrams or ye plain ol' Java.
  • EReference – Use references to provide links between classes. Here's a few things about references:
    • Every class (except the root class) requires exactly one Containment reference that specifies where it belongs to and where it will be stored later on when you save your models to XML files.
    • Set lower and upper bounds on references to control how many instances of a class can be referenced (just like multiplicities in UML class diagram associations).
    • Consider whether a reference should have an opposite reference: a second reference in the other direction to be able to navigate back and forth between the model objects. Let's take two classes as an example to illustrate this: Parentand Child, where Parent can reference multiple Child objects. To be able to ask the Parent about all its children, we would add a reference children from Parent to Child with the containment flag active (that is, Child is part of itsParent). To be able to ask a Child about its Parent, we would add a second reference from Child to Parent with the EOpposite set to the children reference.

For this task, you won't need any more model elements.

One last thing before you get started: While working on your model, save and validate it regularly (Edit -> Validate). This will help you find potential problems with your model while you're still able to fix them easily.

    •  with the EOpposite set to the children reference.

For this task, you won't need any more model elements. You can add these model elements by right-clicking at a package or class and selecting New child... or New Sibling...

One last thing before you get started: While working on your model, save and validate it regularly (Edit -> Validate). This will help you find potential problems with your model while you're still able to fix them easily.

Alternative Route - Graphical Creation of a Metamodel

It is entirely possible to model your metamodel graphically. Since the graphical editing of EMF models has changed in the last Eclipse version, this is only mentioned here as alternative route. To generation an Ecore Diagram proceed as mentioned before. Now, right-click on your model file and select Initialize Ecore Diagram... from the context menu. Create a new Design -> Entities representation and select your package in the following step. You can now create and link all the model elements mentioned in the section before. The changes will be integrated in your Ecore model automatically. However, as mentioned before you don't need to edit your model graphically. This procedure is only described here for the sake of completeness.

Inspecting a Model

Nevertheless you may want to inspect your model graphically. Therefore you installed EcoreViz of the OpenKieler suite. While working on your model, you may right-click on your ecore file and select Vizualize Ecore Model. A new Klighd view will open and display your metamodel. Woha!

Image Added

Code Generation

In order to work with the data structures you specified in your metamodel conveniently, the model must be translated into Java code. For this purpose EMF provides a Java code generation facility that takes the metamodel as its input. Hence, you need a metamodel specified in one of the supported formats. Luckily, Ecore models are a supported format...

...

  1. Create a new empty Plug-In Project.
  2. Add your metamodel project as a dependency of your new project through the Plugin Manifest Editor.
  3. Create a simple Java class that implements a main method. Hint: In a new Java class, simply type main and hit Ctrl+Space. Eclipse content assist will create the method for you.
  4. Import all packages of your metamodel code (i.e., packagenamepackagename.impl, and packagename.util).

Creating a Model

Info
titleNote

...

To talk about programmatically handling models, we will have to assume some sort of design for your model. The design we're assuming here is not the only possible design for your Turing Machines. Don't be alarmed if your model is different.

For instantiation of a model from code you cannot directly use the Java classes generated for the model. Instead, the main package contains interfaces for all of your model object classes. The impl package contains the actual implementation and the util package contains some helper classes. Do not instantiate objects directly by manually calling new. EMF generates a Factory to create new objects. The factory itself uses the singleton pattern to get access to it:

...

Code Block
languagejava
// assuming the model is called "Turing"

...


// and classes are "Model","State" and "Transition"

...


TuringFactory factory = TuringFactory.eINSTANCE;

...


Model myModel = factory.createModel();

...


State state1 = factory.createState();

...


State state2 = factory.createState();

...


Transition trans1 = factory.createTransition();

 

For all simple attributes, there are getter and setter methods:

...

Code Block
languagejava
state1.setName("State 1");

...

Simple references (multiplicity of 1) also have getters and setters:

...

Code Block
languagejava
// assume a transition has simple references to its source and target state

...


trans1.setSourceState(state1);

...


trans1.setTargetState(state2);

...

List references (multiplicity of > 1) have only a list getter, which is used to manipulate the list:

...

Code Block

 

languagejava
 EList<State> states = myModel.getStates();

...


states.add(state1);

...


states.add(state2);

...

With these information out of the way, on we go to some model creation:

...

  1. Add a dependency to the org.eclipse.emf.ecore.xmi plug-in.
  2. Use something like the following code to save the model from above:

    Code Block
     
    languagejava
    // Create a resource set.
    
    ResourceSet resourceSet =
     
     new
     
     ResourceSetImpl();
     
    
    
    // Register the default resource factory -- only needed for stand-alone!
    
    // this tells EMF to use XML to save the model
    
    resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
        
    
        Resource.Factory.Registry.DEFAULT_EXTENSION,
     
     new
     
     XMIResourceFactoryImpl());
     
    
    
    // Get the URI of the model file.
    
    URI fileURI = URI.createFileURI(new
     
     File("myTuringMachine.xmi").getAbsolutePath());
     
    
    
    // Create a resource for this file.
    
    Resource resource = resourceSet.createResource(fileURI);
     
    
    
    // Add the model objects to the contents.
    
    resource.getContents().add(myModel);
     
    
    
    // Save the contents of the resource to the file system.
    
    try
    {
        
    
    {
        resource.save(Collections.EMPTY_MAP);
     
     // the map can pass special saving options to the operation
    
    }
     
     catch
     
     (IOException e)
    {    
     {
        /* error handling */
    
    }
  3. Execute the main method.
  4. Refresh the project.
  5. Hopefully be pleased about the new file in your project. Open the file to see if it contains all elements. Do you understand why opening the model does not correctly open your tree editor? Think about it. (And open it with a text editor.)

...

  1. Create a second class with a main() method.
  2. Load the resource with something like the following code:

     
    Code Block
    languagejava
    // Create a resource set.
    
    ResourceSet resourceSet =
     
     new
     
     ResourceSetImpl();
     
    
    
    // Register the default resource factory -- only needed for stand-alone!
    
    resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
        
    
        Resource.Factory.Registry.DEFAULT_EXTENSION,
     
     new
     
     XMIResourceFactoryImpl());
     
    
    
    // Register the package -- only needed for stand-alone!
    
    // You find the correct name of the package in the generated model code
    
    TuringPackage libraryPackage = TuringPackage.eINSTANCE;
     
    
    
    // Get the URI of the model file.
    
    URI fileURI = URI.createFileURI(new
     
     File("myTuringMachine.xmi").getAbsolutePath());
     
    
    
    // Demand load the resource for this file, here the actual loading is done.
    
    Resource resource = resourceSet.getResource(fileURI,
     
     true);
     
    
    
    // get model elements from the resource
    
    // note: get(0) might be dangerous. why?
    
    EObject myModelObject = resource.getContents().get(0);
     
    
    
    // Do something with the model
    
    if
     
     (
    myModelObject 
    myModelObject instanceof
     
     Model)
    {   
     {
       // Model is the root class of your
    model   
     model
       for(State state: ((Model) myModelObject).getStates()){
          
    
          System.out.println(state.getName());
       }
    
       }
    }
  3. Print the model to the console with something like the following code:

    Code Block
     
    languagejava
    resource.save(System.out, Collections.EMPTY_MAP);

Implementing a Head Controller

This is where all the model design and model generation pays off and where we will establish the link to the second tutorial: you will write an anIHeadController implementation that can simulate Turing Machines specified as models following the metamodel you just designed. Your simulator should be able to execute arbitrary instances of your metamodel.

...

  1. Add a new class to one of your plug-ins named TuringHeadController. Make sure it implements the IHeadController interface we defined in the second tutorial and register it with the appropriate extension point.
  2. Add a new method initialize() to your controller that loads a Turing Machine model from a fixed path.
  3. Implement the nextCommand() and reset() methods:
    • You can access all references and attributes of model elements using generated getter methods.
    • reset() selects a state that is marked as being an initial state of the Turing Machine and saves it as the current state in a private field of the controller. (You did think about modeling initial states, right? If not, don't be frustrated, that's not too big of a deal. Just make the necessary changes to your metamodel and regenerate the code.)
    • nextCommand() analyzes the outgoing transitions of the currently active state and takes the first one that matches the current character. It then selects the target state of that transition as the new active state and returns the actions of the transition as asHeadCommand. If there is no transition that matches the current input, return a command that does nothing.
    • At the beginning of nextCommand(), check if there is an active state. If not, call initialize() and reset() before doing the simulation.
    • Remember that if you add the new controller to the de.cau.cs.rtprak.login.simple plug-in, you will have to add a dependency to the ...turingmodel plug-in and make sure the latter exports the required packages.

...

We won't touch upon EMF's notification mechanism in this tutorial, but we still wanted to mention it. EMF models can be (and are) used as the main models holding the data edited by applications. The notification mechanisms allow you to add observers to the model that get notified upon a definable set of editing operations executed on the model. Feel free so search the Internet for tutorials and introductions to this topic.

 

Info

This tutorial was originally created by Christoph Daniel Schulze and Miro Spönemann for the Eclipse Project WT 12/13.