Author
Laurent GoubetContact
laurent.goubet@obeo.fr
Copyright 2007-2010, Obeo ©
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.
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 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.
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.
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.
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.
Now that the meta-model has been created, we can proceed to generating its implementation.
base package
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*.
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.
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()
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.
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.
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.
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:
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.
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.
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!