Adding new actions to the export menu

Author Laurent Goubet
Contact laurent.goubet@obeo.fr

Copyright 2007-2010, Obeo ©

  1. Adding new actions to the export menu
    1. Source Code
    2. What is the export menu, and why adding new export actions?
    3. The example
    4. Setting up the environment
    5. Step 1 : Designing the meta-model
    6. Step 2 : Defining the meta-model
    7. Step 3 : Generating model code
    8. Step 4 : Prototyping the export
    9. Step 5 : Designing the export action
    10. Step 6 : Providing the export action
    11. Testing the export action

Source Code

To run the example or view the source code for this tutorial you can download the
example plugin
into your workspace and run it in a runtime workbench (see Step 7). You will need to have an Eclipse up and running for EMF Compare for this code
to compile.

What is the export menu, and why adding new export actions?

The export menu is the drop-down menu on the upper-right of EMF Compare GUI, using the classical floppy icon for a save action. On the image below, this
menu is expanded and shows only the default action, As emfdiff

This menu allows users to save the comparison result. Adding actions there can be used to generate comparison reports easily. This tutorial will help
you set a reporting action for a custom meta-model. This action will be made transparent to other users and will only be shown when comparing models
from your own meta-model.

The example

The example used in this tutorial is an HTML export of a model representing a library. This tutorial will go through all the steps from defining
the meta-model to generating code and testing the custom export. If you simply wish to define the export extension point on one of your existing
models, Step directly to Step 5 of this example.

Setting up the environment

This tutorial assumes you already have an Eclipse installed with EMF Compare up and running. See EMF Compare’s latest build dependencies in order to achieve this.

For the purpose of the example you will need to create a new plug-in project:

The page should now look as shown on the image below. Select Finish to create this plug-in.

The PDE editor has opened to allow you to tweak the plugin’s configuration (MANIFEST.MF and plugin.xml). Navigate to its “dependencies” tab and
click the Add button to add dependencies on the plugins listed on the image below.

All of the manipulation we’ll describe in this tutorial will be made within this project.

Step 1 : Designing the meta-model

The first step is to design the meta-model for the models we wish to compare. In UML, the meta-model we’ll consider looks like :

A library contains books, writers and members. Books have a title and a number of pages, and are linked to their author. Writers have a name and are
linked to the books they have written. Members all have a name and an ID, and they can borrow any number of books.

Step 2 : Defining the meta-model

EMF provides a number of ways to create this meta-model, yet this is out of the scope of this tutorial. EMF tutorials such as
Generating an EMF model can help you in
going through this step.

The Ecore file for this meta-model is present in the example plugin and can be downloaded from the CVS with this tutorial and should look like :

We suggest you create the model (or copy the provided file) in a “models” folder at the root of your plug-in project. We’ll assume that you have
named your meta-model library.ecore.

Step 3 : Generating model code

Now that the meta-model has been created, we can proceed to generating its implementation.

  1. Right-click on library.ecore and select New => Other....
  2. On the popup that just appeared, expand Eclipse Modeling Framework and select EMF Model. Click Next.
  3. Fill in the name library.genmodel for this new file and click Next.
  4. Select Ecore model and click Next.
  5. Click the Load button, if an error occurs, the model is not a valid EMF model. Revert to Step 2 and check that you correctly defined the meta-model. Click Next then Finish to create the genmodel for this model.
  6. On the editor that just opened, expand the root (first) node of the tree, and right click the Library node.
  7. Select Show Properties View. A new view should open on your environment.
  8. In the properties view, expand All and select base Package. Fill in org.eclipse.emf.compare.examples.export in its field, as on base package
  9. Right click the root node of the tree.
  10. Successively select Generate Model Code, Generate Edit Code then Generate Editor Code.

Two new packages, org.eclipse.emf.compare.examples.export.library.impl and org.eclipse.emf.compare.examples.export.library.util, have been generated
in your project. Besides, two new projects, org.eclipse.emf.compare.examples.export.library.edit and
org.eclipse.emf.compare.examples.export.library.editor have been created in your workspace.

These newly generated packages and plug-ins correspond to the EMF code of the library meta-model and to a basic editor that can be used to created
library models. This editor will be used later to create the models that will be compared. By default, these models will use the extension *\*.library*.

Step 4 : Prototyping the export

Let’s define the prototype of the report we wish to create on library comparisons. For the purpose of this example, we’ll create a simple HTML report
displaying the added and removed books and members, as well as the borrowed and returned books.

The HTML page will be divided in three. First will come a section for member movements (addition, deletion), then a second section will deal with the
catalog changes (new books, lost books), and a third section will report on borrowals and returns.

We won’t use code generators for this example and the HTML will then be mostly hard-coded with Strings and StringBuffers. This could be improved with
the use of M2T languages but we won’t get into these technologies here as they need tutorials of their own.

Step 5 : Designing the export action

We’ll use a wizard-based action for our report exporting. Export actions must implement the interface org.eclipse.emf.compare.ui.export.IExportAction
which defines five methods:

 void exportSnapshot(ModelInputSnapshot snapshot)
 Image getDisabledImage()                        
 Image getEnabledImage()                         
 String getText()                                
 String getToolTipText()
                          

contains information about the match model and the differences model.

We wish to create our report through a wizard. exportSnapshot will be used to initialize this wizard with the comparison result and opening it on
the user’s interface. As for the icon, we’ll use the same icon for both enabled and disabled state. This icon will be located in the
icons folder of the plug-in.

We’ll create the action class in the package org.eclipse.emf.compare.examples.export.library.action.

  1. Right-click on the root folder of the plugin org.eclipse.emf.compare.examples.export.library.
  2. Select New => Folder and set its name to “icons”.
  3. Copy the icon for your action (either the one above, or one you’ve selected) to this new directory
  4. Right-click on the src folder of the plugin.
  5. | Select New => Package and fill in its name as on the image below
  6. Right-click this new package and select New => Class.
  7. Fill in the name of the class to “LibraryExportAction”.

The java editor should have opened on your new Class. Paste the code below as the code of this action class::

	 public class LibraryExportAction implements IExportAction {
		private final String text;
		private final String toolTipText;
		private final Image image;
		
		public LibraryExportAction() {
			text = "Library report";
			toolTipText = "Exports library comparison result as a report";
			final URL imageURL = LibraryPlugin.getPlugin().getBundle().getEntry("icons/libraryexport.gif");
			image = ImageDescriptor.createFromURL(imageURL).createImage();
		}
		
		public String getText() {
			return text; 
		}
	
		public String getToolTipText() {
			return toolTipText;
		}
	
		public Image getDisabledImage() {
			return image;
		}
	
		public Image getEnabledImage() {
			return image;
		}
	
		public void exportSnapshot(ModelInputSnapshot snapshot) {
			final LibraryExportWizard wizard = new LibraryExportWizard();
			final IWorkbench workbench = PlatformUI.getWorkbench();
			
			wizard.init(workbench, snapshot);
			final WizardDialog dialog = new WizardDialog(workbench.getActiveWorkbenchWindow().getShell(), wizard);
			dialog.open();
		}
	 }

All needed variables are initialized within the default constructor with static values :

The method exportSnapshot is implemented by creating a custom wizard and passing it the snapshot via its init method. We need to create this
wizard : repeat the steps above to create a package named “org.eclipse.emf.compare.examples.export.library.wizard” alongside the “\*.action” one.
Once done, copy the two classes
LibraryExportWizard
and
LibraryExportWizardPage
within this new package.

LibraryExportWizardPage is the page that will be displayed by the wizard. It allows us to define our own validation rules::

 protected boolean validatePage() {
	boolean result = super.validatePage();
	if (result) {
		final String fileName = getFileName();
		if (fileName.endsWith(".html")) {
			setErrorMessage(null);
			return true;
		}
		setErrorMessage("File name must end in '.html'");
		return false;
	}
	// This will return false
	return result;
 }


Our page will then be valid if and only if the file the user has chosen ends with the extension “\*.html”. The wizard will output an error message
and prevent file creation otherwise.

LibraryExportWizard is the core of our export action. This wizard will take care of creating the result file and generating the HTML code within
it. Let’s take a more detailed look at its methods of interest.

to generate the HTML table displaying to the user the books that have been returned::
    private String generateReturnedTable() {
    	String returnedBooks = new String();
    	final TreeIterator<EObject> iterator = input.getDiff().eAllContents();
    	while (iterator.hasNext()) {
    		final EObject next = iterator.next();
    		if (next instanceof RemoveReferenceValue) {
    			final EReference target = ((RemoveReferenceValue)next).getReference();
    			if (target.getFeatureID() == LibraryPackage.MEMBER__BORROWED_BOOKS) {
    				// We need to create the table headers
    				if (returnedBooks.length() == 0) {
    					returnedBooks += "<table><tr>";
    					returnedBooks += "<td class=\"header\" colspan=\"2\">Returned Books</td></tr>";
    					returnedBooks += "<tr><td class=\"header\">Title</td><td class=\"header\">Member</td></tr>";
    				}
    				final Book returned = (Book)((RemoveReferenceValue)next).getRightRemovedTarget();
    				final Member member = (Member)((RemoveReferenceValue)next).getRightElement();
    				returnedBooks += "<tr><td>";
    				returnedBooks += returned.getTitle();
    				returnedBooks += "</td><td>";
    				returnedBooks += member.getName();
    				returnedBooks += "</td></tr>";
    			}
    		}
    	}
    	// Closes the table if we found returned books
    	if (returnedBooks.length() > 0)
    		returnedBooks += "</table>";
    	return returnedBooks;
    }

   What does this code do? It iterates over the whole differences model (*input.getDiff().eAllContents()* creates an iterator over direct and
   indirect children of the DiffModel) and searches for elements corresponding to removed references (*RemoveReferenceValue*). For each of these
   removals, we'll check that the target reference is **borrowedBooks** of the class "Member". When we find such a reference, we'll create an
   HTML table containing the title of the returned book and the name of the member that had borrowed it.

Now that we have both defined the classes backing up our export action and the model’s code, we can set the extension point to provide EMF Compare
with our report action.

Step 6 : Providing the export action

Export actions can be added through the extension point org.eclipse.emf.compare.ui.export. This extension point accepts three parameters, two of
which being mandatory. id is the unique identifier of your extension, class is the fully qualified name of the action class implementing
org.eclipse.emf.compare.ui.export.IExportAction. The third and optional parameter is the file extension on which this export action will be
enabled. We’ve prepared all of the required classes in the above steps: let us now configure this extension point:

  1. Double-click on the file META-INF/MANIFEST.MF of the org.eclipse.emf.compare.examples.export.library plugin.
  2. | Select the extensions tab
  3. Click on Add, select the extension point org.eclipse.emf.compare.ui.export and click Finish.
  4. On the right part of the editor now appear three fields labeled ID, class and fileExtension corresponding to the parameters described above.
  5. Fill in the ID with a unique identifier (leaving it as-is should be enough).
  6. Click on Browse... and enter the name of the action Class "LibraryExportAction", select it on the lower part and click OK.
  7. Set the fileExtension to be library. The tab should now look like

That’s it! Your export action will now be automatically added to the export menu of EMF Compare’s GUI for each “library” file comparison.

Testing the export action

You will need to launch a runtime workbench to test your action. Expand the menu Run and select Open Run Dialog..., double click "Eclipse
Application" and click Run.

A new, empty workbench should have opened. Select New => Other... => Plugin Project and name this project “library.test”. Click Finish to
create the project we’ll use to test all of the code we prepared earlier.

  1. Right-click on the project and select New => Other....
  2. Expand Example EMF Model Creation Wizard.
  3. You should see an element Library Model in this category. Select it and click Next.
  4. Name your library model v1.library and click Next.
  5. This page prompts you for the Model Object, select Library and click Finish.

You can now create the first version of the library model. Create some Writers, Members and Books, set some borrowals, then copy/paste your model,
naming the new one v2.library and make modifications to this new version (you can also commit the model on SVN/CVS, the goal of this operation is
to have the first version saved somewhere while still modifying another version of it).

If you used the meta-model provided in Step 2 for the generation, the samples folder>of the CVS contains two versions of a sample library.

You now need to tell EMF Compare that library files should be compared as models. See the EMF Compare FAQ
to achieve this.

You can now launch a comparison of your libraries by selecting both models, then selecting Compare With => Each Other as shown below

You should now be looking at the comparison result of your two models through EMF Compare’s GUI. Expanding the export menu should display the **library
report** action as shown below. This action will not be displayed to the user when comparing files of an extension distinct from \.library*.

Selecting this action will make the new file wizard pop up with result.html as the default name for the result. Select the folder where you wish
the report created, then click Finish. The file has now been created in the selected folder. Double click on it to see the report about this
particular comparison. If you have used the two library models provided in the zip file above, the report looks like :

You are now ready to customize this sample report, or to define your own!