Child pages
  • The Eclipse Modeling Framework

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Warning
titleWarning

This tutorial is a work-in-progress. Don't start with it yet!

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 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.

Once you're done with this tutorial, you will have an application that looks something like this:

Note
titleToDo

Insert screenshot of final application.

Note
titleToDo

Insert link to presentation slides.

Table of Contents

Preliminaries

The Eclipse Modeling Image Added

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

Table of Contents

Preliminaries

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!

...

Saving and Loading Models

 

 

 

 

 

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 IHeadController 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.

Simulating Turing Machines

Right, time to write some simulation code. Go grab a cup of coffee and start working through the following steps:

...

We will now look at working with EMF models, editing them through Java code instead of the GUI. We will also load and save them from / to XMI resources.

Creating a Test Project

We need a project to test with. If you already know your way around JUnit tests, you can skip creating a test project and just use the generated test project. If not, do the following:

  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., packagename, packagename.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. Create a valid model in the main() method with at least 5 Objects.
  2. Run the main() method by right-clicking its class and selecting Run as -> Java Application. Note that this runs your main() method as a simple Java program, not a complete Eclipse application. EMF models can be used in any simple Java context, not just in Eclipse applications.

Saving a Model

EMF uses the Eclipse Resource concept to save models to files and load models from files. It can use different Resource Factories that determine how exactly models are serialized. We will use the XMIResourceFactoryImpl to save our models to XML files:

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

Loading a Model

  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 instanceof Model) {
       // Model is the root class of your 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 IHeadController 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.

Simulating Turing Machines

Right, time to write some simulation code. Go grab a cup of coffee and start working through the following steps:

  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 HeadCommand. 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.

...

  1. Create a Turing Machine model using the tree editor. Assuming that the initial head position is 1, the machine shall copy the input text infinitely often. That is, if the tape initially contains the word "hello", the machine should generate "hellohellohellohe..." You might remember this task from the second tutorial. To avoid an explosion of the number of states, select a rather small input alphabet for your machine, e.g. h, e, l, and o.

  2. Save the model to the fixed path defined in your controller.

  3. Select the new head controller in the Tape view and test it with input from your editor.

EMF Notifications (Optional)

 

 

 

...

  1. initially contains the word "hello", the machine should generate "hellohellohellohe..." You might remember this task from the second tutorial. To avoid an explosion of the number of states, select a rather small input alphabet for your machine, e.g. h, e, l, and o.

  2. Save the model to the fixed path defined in your controller.

  3. Select the new head controller in the Tape view and test it with input from your editor.

EMF Notifications

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.