Customizations

In this chapter we will describe how EMF Parsley lets you customize the standard behaviours. A DSL is provided to easily customize most common features, but you can also customize all aspects manually (i.e., in directly Java). As a matter of fact each customization is explained in a single section, with the details on how to do that with the DSL (if available) and in Java.

If you want to provide a specific implementation in Java, you can use the Google Guice injection mechanism, by overriding the specific class with your own implementation. Note that in some cases an explicit constructor is needed, with the @Inject annotation to make Guice correctly works; when this is the case, the base class will already have such a constructor and you will only need to override it, but you will also need to add the @Inject annotation explicitly.

Although one can specify any Guice Module, EMF Parsley ships with some default base class modules that should be used for specifying custom Guice bindings. The default base class is EmfParsleyGuiceModule (src) that is suitable to be used in an OSGI environment, like Eclipse itself or RAP (see also Eclipse 4.x & RAP). Our project wizards will automatically use such module as the base class. For CDO we have a specialized base module.

We also have a module to be used in a non OSGI environment, e.g., a pure Java environment: EmfParsleyJavaGuiceModule (src) (this is the base class of EmfParsleyGuiceModule (src)). This is useful also for testing purposes, for writing plain Junit tests (i.e., not Plug-in Junit tests). This is also used in our testing framework (see EMF Parsley Testing Framework).

TODO: Explain polymorphic implementations

Dependency Injection With Google Guice

All Parsley components are assembled by means of Dependency Injection (DI). This means that whenever some code is in need for functionality (or state) from another component, one just declares the dependency rather then stating how to resolve it, i.e. obtaining that component.

For example, when some code wants to use a label provider, it just declares a field (or method or constructor) and adds the @Inject annotation:

class MyView extends ViewPart {

    @Inject
    private ILabelProvider labelProvider;

}

It is not the duty of the client code to care about where the actual ILabelProvider comes from or how it is created. When the above class is instantiated, Guice sees that it requires an instance of ILabelProvider and assigns it to the specified field or method parameter. This of course only works, if the object itself is created by Guice. In Parsley almost every instance is created that way and therefore the whole dependency net is controlled and configured by the means of Google Guice.

Guice of course needs to know how to instantiate real objects for declared dependencies. This is done in so called Modules. A Module defines a set of mappings from types to either existing instances, instance providers or concrete classes. Modules are implemented in Java. Here's an example:

public class MyGuiceModule extends AbstractGenericModule {

    @Override
    public void configure(Binder binder) {
        super.configure(binder);
        binder.bind(ILabelProvider.class).to(MyLabelProvider.class);
        binder.bind(...
    }

With plain Guice modules one implements a method called configure and gets a Binder passed in. That binder provides a fluent API to define the mentioned mappings. This was just a very brief and simplified description. We highly recommend to have a look at the Google Guice website to learn more.

Module API

Parsley comes with a slightly enhanced module API (this was inspired by Xtext, so, if you are already familiar with the enhnaced Guice module API of Xtext, you can use Parsley API right away).

The enhancement we added to Guice's Module API is that we provide an abstract base class, which reflectively looks for certain methods in order to find declared bindings. The standard base class is EmfParsleyGuiceModule (src), which can be used in a standard Eclipse OSGI environment. If you are using CDO, it is better to use as base class CDOEmfParsleyModule, which has defaults that better fit a CDO environment. If you do not need OSGI, you can use EmfParsleyJavaGuiceModule (src) (e.g., to run tests with plain Junit, see also Testing Framework).

The most common kind of method is

public Class<? extends ILabelProvider> bindILabelProvider() {
    return MyLabelProvider.class;
}

which would do the same as the code snippet above. It simply declares a binding from ILabelProvider to MyLabelProvider. That binding will make Guice instantiate and inject a new instance of MyLabelProviderProvider whenever a dependency to ILabelProvider is declared.

There are two additional kinds of binding-methods supported. The first one allows to configure a provider. A Provider is an interface with just one method:

public interface Provider<T> extends javax.inject.Provider<T> {

  /**
   * Provides an instance of {@code T}. Must never return {@code null}.
   */

  T get();
}

This one can be used if you need a hook whenever an instance of a certain type is created. For instance if you want to provide lazy access to a singleton or you need to do some computation each time an instance is created (i.e. factory). If you want to point to a provider rather than to a concrete class you can use the following binding method:

public Class<? extends Provider<ILabelProvider>> provideILabelProvider() {
    return MyLabelProviderFactory.class;
}

The last kind of binding allows to inject values in Parsley components; here are some examples of such bindings implemented in the base class of Parsley Guice module:

/**
 * The String constant for Content Assist Shortcut
 */

public String valueContentAssistShortcut() {
    return "Ctrl+Space";
}

/**
 * The String constant used as a ellipses for Iterable string representation
 * when it is too long
 */

public String valueIterableStringEllipses() {
    return "...";
}

/**
 * The list of Integer weights for a table's columns
 */

public List<Integer> valueTableColumnWeights() {
    return Collections.<Integer>emptyList();
}

/**
 * The int constant defining the Sash style in a TreeFormComposite
 */

public int valueTreeFormSashStyle() {
    return SWT.VERTICAL;
}

Specify Guice Bindings in the DSL

Guice bindings can be specified directly in the DSL, in the bindings section.

In this section you can specify bindings of all the three above kinds with type, provide and value respectively, e.g.,

bindings {
    type ILabelProvider -> MyLabelProvider
    type ... -> ...
    provide ProposalCreator -> MyProposalCreatorProvider
    ...
    value int TreeFormSashStyle -> SWT.HORIZONTAL
}

We strongly suggest you use the content assist to discover default bindings, since they also show Javadoc for each default binding:

Providers

Viewer Label Provider

The Jface Label Provider allows to specify the representation of a given Object. EMF Parsley provides an implementation that uses the information provided via the DSL, as you can see in the snippet below. We allow customization for text, image, font, and foreground and background color.

labelProvider{
    text {
        Book -> "Book:"+title
        Borrower -> "Borrower: "+firstName
    }
    image {
        Book -> "book.png"
    }
    font {
        Book -> // must return a org.eclipse.swt.graphics.Font
    }
    foreground {
        Book -> // must return a org.eclipse.swt.graphics.Color
    }
    background {
        Book -> // must return a org.eclipse.swt.graphics.Color
    }
}

However if you want to customize the label provider in Java, you need to provide an implementation of ILabelProvider and injecting it in the spefic module by overriding bindILabelProvider.

EMF Parsley provides such a base implementation with the class ViewerLabelProvider (src) that is meant to be subclassed by the programmer to provide specific implementations like in the example below. Our label provider also implements IFontProvider and IColorProvider, so that you can customize also the font, the foreground and the background color.

This class, like many others in our framework, relies on the polymorphic dispatcher idiom to declaratively specify text and image representations for objects. It boils down to the fact that the only thing you need to do is to implement a method that matches a specific signature: text and image for the String representation and the image, respectively. These methods will need to specify as parameter the type of the object to represent. For the image, you can either specify an image filename or an Image object. File names for images are assumed to refer to files in the icons folder of the containing plug-in.

public class CustomLibraryLabelProvider extends ViewerLabelProvider {

    @Inject
    public CustomLibraryLabelProvider(AdapterFactoryLabelProvider delegate) {
        super(delegate);
    }

    public String text(Book book) {
        return "Book: " + book.getTitle();
    }

    public String image(Book book) {
        return "book.png";
    }

    public Font font(Book book) {
        return // must return a org.eclipse.swt.graphics.Font
    }

    public Color foreground(Book book) {
        return // must return a org.eclipse.swt.graphics.Color
    }

    public Color background(Book book) {
        return // must return a org.eclipse.swt.graphics.Color
    }

    public String text(Borrower b) {
        return "Borrower: " + b.getFirstName();
    }
}

Viewer Content Provider

As in Jface, the Content Provider is used to get the elements to represent in a tree and their children (as detailed in Table Viewer Content Provider, for tables we use a different content provider). EMF Parsley provides an implementation that uses the DSL as in the code below.

viewerContentProvider{
    elements{
        Library -> books
    }
    children{
        Library -> books
        Book b-> {
            new ArrayList()=>[
                add(b.author)
                addAll(b.borrowers)
            ]
        }
    }
}

The developer can also provide a specific implementation of IContentProvider by injecting it in the spefic module (TODO). EMF Parsley provides a base implementation with the class ViewerContentProvider (src) that can be easily used to specify the children of all object on the tree, like in the example below (again, this uses the polymorphic dispatch idiom).

public class CustomLibraryViewerContentProvider extends ViewerContentProvider {

    @Inject
    public CustomLibraryViewerContentProvider(AdapterFactory adapterFactory) {
        super(adapterFactory);
    }

    public Object elements(Library library) {
        return library.getBooks();
    }

    public Object children(Library library) {
        return library.getBooks();
    }

    public Object children(Book book) {
        ArrayList<Object> children = new ArrayList<Object>();
        Writer author = book.getAuthor();
        if (author != null) {
            children.add(author);
        }
        children.addAll(book.getBorrowers());
        return children;
    }
}

Table Viewer Content Provider

For table viewers we use a customized content provider, which inherits from the one described in Viewer Content Provider; for tables we only need to specify how the root elements are computed (no children are needed for tables).

This content provider, TableViewerContentProvider (src), must be configured with the EClass (src) of the objects that will be shown in the table, so the setEClass must be called before this content provider is used. This setup is already automatically performed in views that are shipped with Parsley; in case you need to setup a table viewer yourself with this content provider, we strongly suggest you inject a TableViewerContentProviderFactory (src) and use its method createTableViewerContentProvider(EClass type).

With the information about the EClass (src) this content provider is able to automatically retrieve all the contents of that type from a Resource (src) or any EObject (src), by retrieving inspecting all the containment references of that type, recursively in the model.

In case you want to optimize the retrieval of contents, or in case you want to show elements of the specified type which are not contained in an EObject (src) (because they are references with containment set to false), you can inject your own custom TableViewerContentProvider (src) and define elements methods (again, this uses the polymorphic dispatch idiom).

In the DSL this can be done using the specific section.

tableViewerContentProvider{
    elements{
        Library lib -> {
            // this is just an optimization: since books are contained in the library
            // the default content provider will retrieve them automatically
            lib.books
        }
        Writer w {
            // writers' books would not be retrieved by the default content provider
            // since they are NOT 'contained' in a writer.
            w.books
        }
    }
}

IMPORTANT: customizations specified in a ViewerContentProvider (src) will NOT be reused by a TableViewerContentProvider (src).

Table Label Provider

The Jface Table Label Provider allows to specify the representation of a given cell in a table. EMF Parsley provides an implementation that uses the information provided via the DSL, as you can see in the snippet below. We allow customization for text, image, font, foreground and background color for a given object's feature (which corresponds to a table cell), and also font, and foreground and background color for the entire row.

Concerning fonts and colors, a customization for a single cell has the precedence over the customization of an entire row.

Here's an example.

tableLabelProvider {
    text {
        Library:name -> 'Name' // constant
        Library:books -> 'Books' // constant
        Writer:lastName -> name.toFirstUpper // the implicit param is an EStructuralFeature
    }
    
    image {
        Book: author -> 
            if (author.name.nullOrEmpty) 
                "noname.gif"
            else
                new ImageData("writer.jpeg")
    }
    
    font {
        Library : name -> JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT)
    }
    
    foreground {
        Library : books -> Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)
    }
    
    background {
        Library : address -> Display.getCurrent().getSystemColor(SWT.COLOR_GREEN)
    }
    
    rowFont {
        Library -> JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT)
    }
    
    rowForeground {
        Library -> Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)
    }
    
    rowBackground {
        Library -> Display.getCurrent().getSystemColor(SWT.COLOR_GREEN)
    }
}

Features Provider

EMF Parsley uses this kind of provider wherever a list of features is requested for a certain EClass. The default is to return the list of all the features in the EClass, but the programmer can customize it (for instance, by returning only a superset, or in a different order) on an EClass-based strategy. Thus you can use the DSL to specify that list, as in the snipped below.

featuresProvider{
    features{
        Book -> title, author, category, pages
    }
}

If you want to customize it in Java, there are more ways to customize this behaviour, but we need to go deep in some details of the Feature Provider implementation.

When the framework builds components according to the EStructuralFeature (src)s of a given EClass (src) it relies on an injected FeaturesProvider (src). The default behavior is to simply return all the features of the a given EClass, in the order they are defined in the EClass, as implemented by the method defaultFeatures in FeaturesProvider (src).

You can set the mappings, i.e., specify the structural features you want to be used given an EClass, by implementing the method buildMap, which receives the FeaturesProvider.EClassToEStructuralFeatureMap (src) that can be filled with the method mapTo; for instance, using the EMF extended library example, this customization will return only the name and address features for Library, the firstName, lastName and address for Person, and the firstName, lastName and books (but not address) for Writer (which inherits from Person).

import static org.eclipse.emf.examples.extlibrary.EXTLibraryPackage.Literals.*;
import org.eclipse.emf.parsley.ui.provider.EStructuralFeaturesProvider;

public class LibraryEStructuralFeaturesProvider extends
        FeaturesProvider {

    @Override
    protected void buildMap(EClassToEStructuralFeatureMap map) {
        super.buildMap(map);
        map.mapTo(LIBRARY,
                LIBRARY__NAME, ADDRESSABLE__ADDRESS);
        map.mapTo(PERSON, PERSON__FIRST_NAME, PERSON__LAST_NAME, ADDRESSABLE__ADDRESS);
        map.mapTo(WRITER, PERSON__FIRST_NAME, PERSON__LAST_NAME, WRITER__BOOKS);
    }
}

Another possibility is to build a map which relies on Strings both for the EClass (src) and for the list of EStructuralFeature (src); note that the name of the EClass (src) should be obtained by using getInstanceClassName(); you can also combine the two approaches.

Table Features Provider

As an extension, you can use the TableFeaturesProvider (src): the customizations will be applied only to Tables, not to Forms.

If there are no specific customization in the TableFeaturesProvider (src), we fall back to FeaturesProvider (src).

Feature Caption Provider

The FeatureCaptionProvider (src) provides captions for the features in Tables and Forms. Here you can see an example of the DSL.

featureCaptionProvider{
    text{
        Book:author -> "Written by:"
        Writer:name -> "Name:"
    }
}

If you want to customize it in Java, you need to derive from FeatureCaptionProvider (src). It can be customized, with injection section GuiceBindings: this way you can customize the caption label for controls in a form, dialog, and the headers in a table's column. The framework uses a polimorphic mechanism to find customizations: it searches for methods with a specific signature: the name is built by the string 'text' followed by the EClass and the EStructuralFeature. All parts of the name are separated by an underscore character and the method must accept a parameter of type EStructuralFeature.

In the following example we specify the caption text for the feature 'Author' of Book and the feature 'Name' for Writer.

public String text_Book_author(final EStructuralFeature feature) {
    return "Written by:";
}

public String text_Writer_name(final EStructuralFeature feature) {
    return "Name:";
}

If no customization is provided, the text will be computed using the feature's name. This will always be the default for table column headers (since no object is available when building the table); while for form and dialog captions we use a slightly different default strategy, as shown in Form and Dialog Feature Caption Provider.

Form and Dialog Feature Caption Provider

The FormFeatureCaptionProvider (src) (DialogFeatureCaptionProvider (src), respectively) can be used if you want to define the description only for forms (for dialogs, respectively). For example using the Tree Form your definition will not be used in the tree.

In this case you can also define a method the returns directly the Label, like in the example below. In such methods there is another parameter that is the parent composite (that is automatically passed by the framework).

public Label label_Writer_name(Composite parent, EStructuralFeature feature) {
    Label label = defaultLabel(parent, feature);
    label.setBackground(getFormToolkit().getColors().getColor(IFormColors.TITLE));
    return label;
}

In the DSL you have the corresponding two sections available:

formFeatureCaptionProvider{
    text{
        Book:author -> "Written by:"
    }
    label{
        Writer:name -> createLabel(parent, "Name")
    }
}

dialogFeatureCaptionProvider{
    text{
        Book:author -> "Author:"
    }
    label{
        Writer:name -> createLabel(parent, "Writer's name")
    }
}

If there is no customization in the FormFeatureCaptionProvider (src) (DialogFeatureCaptionProvider (src), respectively), the following steps are executed to create the text for the label:

Proposal Provider

Some controls use a list of proposals to help the end user experince: for example a single value reference feature will be rendered by default with a combo box, automatically filled with all the possible targets for that reference; similarly for Enum features. You can customize the proposals, and you can specify proposals also for simple text fields (a content assist dialog will show up for text fields).

For each feature you can specify a list of proposals via the DSL. In the example below, we first compute the default proposals for that feature and then we filter the proposals.

proposals{
    Book:author -> {
        defaultProposals(feature).
            filter(Writer).
            filter[name.startsWith("F")].toList
    }
}

This customization can be done also in Java, by extending the class ProposalCreator (src) and implementing the method public List<?> proposals_Book_author(Book book) {...}. This method follows the same convention on the signature name as explained in Feature Provider.

Contextual Menu

A context menu can be added to any StructuredViewer by using an injected ViewerContextMenuHelper (src). This provides some methods for adding the context menu

@Inject ViewerContextMenuHelper contextMenuHelper;
(...)

// simplest form
contextMenuHelper.addViewerContextMenu(viewer);

// if you have an AdapterFactoryEditingDomain already
contextMenuHelper.addViewerContextMenu(viewer, editingDomain);

// if you're inside an IWorkbenchPart
contextMenuHelper.addViewerContextMenu(viewer, editingDomain, part);

The contents of such menu are built automatically by the framework or customized by the programmer, as shown in the next section.

Menu Builder

EMF Parsley uses the standard EMF.Edit features to build the contextual menus of viewers (thus you will get by default the standard "New Child" and "New Sibling" sections in the context menu).

You can customize context menus on a per class basis by extending the EditingMenuBuilder (src) (and injecting it in the Guice module). However, we suggest to use the DSL for this task, as detailed in the following.

EMF Parsley logically separates the menu into 2 parts. The first section contains all common edit commands such as copy and paste. The second section contains EMF specific commands, such as for example new child. You can use the DSL to fully customize the menu, as in the example below.

menuBuilder{
    menus{
        Library-> #[
            submenu("Edit",#[
                actionCopy,
                actionCut,
                separator,
                actionPaste
            ])
        ]
    }
    emfMenus{
        Library lib -> #[
            actionAdd("Add a new book", lib.books,
                EXTLibraryFactory.eINSTANCE.createBook)
        ]
    }
}

For each EClass of your meta-model you can specify a list of menu items (the #[] is the Xbase syntax for a list literal) Content assist is available to select the editing actions, the separator and also methods for EMF menu part.

In the emfMenus section, you can use some methods of the EditingMenuBuilder (src) class, as detailed in the following.

The method actionAdd, specifying the label for the menu, the containment list in the model, and the object to add in such list when the menu is selected (Note that it is up to you to specify a containment list); the DSL will issue an error if the object cannot be added to the list (because it is not of the right type). The object should be created using the standard EMF API (i.e., using the EMF factory for your model).

If you want to specify further initialization instructions for the created object you can pass a lambda expression as another argument to actionAdd: that lambda will be executed ONLY after the menu has been selected, i.e., ONLY after the created object is part of the resource:

emfMenus{
    Writer w -> #[
        actionAdd("Add a new book for the writer",
            (w.eContainer as Library).books,
            EXTLibraryFactory.eINSTANCE.createBook,
            [ book | book.title = "A new book" ]
        )
    ]
}

IMPORTANT: do not set any reference feature of the created EObject in the lambda, i.e., do not do something like the following

emfMenus{
    Writer w -> #[
        actionAdd("Add a new book for the writer",
            (w.eContainer as Library).books,
            EXTLibraryFactory.eINSTANCE.createBook,
            // WRONG: don't do that
            [ book | book.author = w ]
        )
    ]
}

This will not work if you undo the command: the writer that has been added to the library will be removed, and the book.author will be a dangling reference! as a consequence the resource cannot be saved.

If you want to implement more complex menu commands that do not only add elements to a container, you can use the method actionChange, specifying the label for the menu, the model's element that will be affected by the changes specified as a lambda expression (the third argument). The lambda expression will also get the specified model's element as argument. The model's element can also be the whole resource itself (formally, it can be any EMF Notifier (src)).

It is crucial to specify the correct model's element to make undo/redo work correctly: all the modifications performed in the lambda expression that concern the specified element will be recorded, in order to implement undo/redo.

For example, this command, that will add a new book to the library, and sets its author to the selected writer will work as expected, and selecting undo will effectively remove the book from the writer's list and from the library:

emfMenus{
    Writer w -> #[
        actionChange("New book", w.eContainer as Library,
            [
                library |
                val book = factory.createBook
                library.books += book
                book.title = "A new book"
                book.author = w
            ]
        )
    ]
}

This works since we specify the containing library as the model's element, thus, all modifications that concern the library will be recorded.

On the contrary, this variant, will perform exacty the same actions on the model, but selecting undo will only remove the book from the writer's list, and the book will still be present in the library:

emfMenus{
    Writer w -> #[
        // in this variant undo will only unset the book's author,
        // but it will not remove the added code from the library
        // since we record changes concerning the writer only
        actionChange("New book (variant)", w,
            [
                writer |
                val library = writer.eContainer as Library
                val book = factory.createBook
                library.books += book
                book.title = "A new book"
                book.author = w
            ]
        )
    ]
}

This happens since we specified the writer as the model's element, thus only the changes that concern the writer will be undone.

Drag and Drop

Drag and drop can be added to any StructuredViewer by using an injected ViewerDragAndDropHelper (src), using its methods addDragAndDrop.

Currently, drag and drop is completely delegated to EMF.Edit.

Factories

Widget Factory

The actual creation of text field, buttons, labels, etc. is delegated to an implementation of IWidgetFactory (src), which has several methods like createText, createLabel, etc. We provide two implementations of such interface

Usually, you do not need to customize such factories, which are used internally by the framework, like in section FormControlFactory and section DialogControFactory.

You may want to customize such factories in case all your controls must have a specific style; in such case, just inherit from our base classes (there is no DSL section for such customizations, since they can be made in plain Java easily) and bind such custom implementations in the Guice module.

Form Control Factory

EMF Parsley lets you customize the form controls via the DSL as in the following example.

formControlFactory {
    control {
        Library : name -> { }
        Writer : books -> 
            createLabel(
                books.map[title].join(", "))
        Writer : name -> { createLabel(parent, "") }
            target { observeText }
        Writer : firstName -> 
            toolkit.createLabel(parent, "")
            target observeText(SWT.Modify)
        Borrower : firstName -> {
            createText(firstName, SWT.MULTI, SWT.BORDER,
                                SWT.WRAP, SWT.V_SCROLL)
        }
    }
}

For each pair EClass, EStructuralFeature you can either simply return a Control or specify also the target for the databinding (see some examples above). If you want to customize the controls in Java, you can extend the class FormControlFactory (src). Using the same polimorphic mechanism of the labels, the programmer can write a method with name starting with 'control' followed by the names of the EClass and of the EStructuralFeature undescore-character-separated.

The method must accept as parameters the Source Observable and the feature; the superclass' method bindValue can be used for databinding. The DatabindingUtil (src) utility class can be used for creating the target observable. Here's an example

public Control control_Writer_name(IObservableValue source, EStructuralFeature f) {
    // Creating the control
    Text text = getToolkit().createText(getParent(), "");
    text.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
    text.setBackground(getToolkit().getColors().getColor(IFormColors.TITLE));
    // Perform databinding; params: feature, target, source
    bindValue(f, DatabindingUtil.observeText(text), source);
    return text;
}

Another form of the method's parameters is also taken into consideration during the polymorphic dispatch. This is the old form and it will be likely deprecated in the future: the method must accept as parameters the DataBinding Context and the Feature Observable that can be used for databinding. Here's an example:

public Control control_Writer_name(DataBindingContext dbc, IObservableValue featureObservable) {
    // Creating the control
    Text text = getToolkit().createText(getParent(), "");
    text.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
    text.setBackground(getToolkit().getColors().getColor(IFormColors.TITLE));
    // Binding the control to the feature observable
    dbc.bindValue(SWTObservables.observeText(text, SWT.Modify), featureObservable);
    return text;
}

Dialog Control Factory

If you want to customize controls in Dialog, you can use the specific DSL section dialogControlFactory:

dialogControlFactory {
    control {
        ...
    }
}

This customization is exactly as in the case of the form of the previous section.

Editing Domain

The concept of EditingDomain (src) is crucial for editing EMF models; we refer to the EMF.Edit documentation for further details. In particular, the editing domain keeps track of commands executed on an EMF model, thus enabling undo/redo mechanisms and "dirty state" management for saveable parts.

EMF Parsley aims at hiding the management of the editing domain, so that everything should work smoothly and as expected, in an automatic way. In particular, it is rare that you need to perform customizations on this mechanisms. However, there might be cases when you need to be aware of this concept, especially if you need to use one of our customizations. Moreover, you need to be aware of some of the assumptions that EMF Parsley automatic mechanisms rely on (we inherit these assumptions from EMF.Edit itself).

First of all, all the EMF Resource (src)s that you want to edit with EMF Parsley must be contained in a ResourceSet (src), which, in turn, must be contained in an EditingDomain (src). This is achieved automatically when using our ResourceLoader, Resource Loader.

Two resources loaded with different resource loaders will be contained in two different editing domains. Two resources loaded with the same resource loader will be in the same resource set and use the same editing domain.

Our default implementation of editing domain uses the EMF AdapterFactoryEditingDomain (src), so that all the EMF.Edit default mechanisms will work correctly. In particular, our customization uses Google Guice mechanisms (see Dependency Injection With Google Guice), thus if you need an editing domain in your own views all you have to do is to inject it, e.g.,

@Inject
private EditingDomain editingDomain;

Editing Domain Provider

If you need to provide a custom implementation of the editing domain (for example, because you want to use a transactional editing domain), you need to implement a custom Google Guice Provider and in your Guice module override this method:

public Class<? extends Provider<EditingDomain>> provideEditingDomain() {
    return DefaultEditingDomainProvider.class;
}

Such custom provider will then have to create an editing domain and return it.

We have some custom editing domain providers that might be useful in some situations:

Editing Domain Finder

All the EMF Parsley saveable views and editors will have their own editing domain (modulo what we explained in Editing Domain Provider).

The EMF Parsley views that react on selection do NOT have a preset editing domain, since they will represent (and possibly edit) EMF objects selected in some other views, i.e., such objects can be contained in resources of different resource sets (and different editing domains). Thus, the editing domain of the currently shown object is dynamically retrieved through an injected EditingDomainFinder. This default implementation basically delegates to the standard EMF.Edit mechanisms for retrieving the editing domain. In cases where the editing domain cannot be found (e.g., because the object is not contained in a resource, or its resource is not contained in a resource set, or its resource set is not contained in an editing domain), then editing will not be possible (i.e., context menus section ContextualMenu and drag and drop Drag and Drop will not work).

You can provide and bind a custom implementation of the EditingDomainFinder which is particularly useful if you manage a transactional editing domain.

This is required only in some specific and advanced scenarios.

In standard situations you will not have to worry about that, and editing mechanisms will work out of the box, including dragging an element from one view into another view, provided they are in the same resource set and such drag and drop makes sense.

Resources

Concerning saving objects, there are some specific parts that can be customized:

Resource Loader

The class ResourceLoader (src) can be used to handle resource loading. This class uses internally the Resource Manager.

Resource Manager

Tasks concerning an EMF Resource (src) are delegated to ResourceManager (src).

One of such tasks is initializing the resource, e.g., when, after loading, it is found empty. You can derive from this class (and bind it in the Guice module) and provide a custom implementation of the method initialize.

Saving a resource is also delegated to this class, using the method save, which is expected to return a boolean value representing whether saving has succeeded (the default implementation simply saves the resource and returns true).

In the DSL, you can specify a resourceManager block, and within that block you can specify initializeResource and saveResource, which correspond to inizialize and save methods, respectively. In both cases, inside the block expression, the resource is available with the name it; for example

import org.eclipse.emf.parsley.examples.library.EXTLibraryFactory

...

resourceManager {
    val EXTLibraryFactory libraryFactory = EXTLibraryFactory.eINSTANCE;
    
    initializeResource {
        // it is of type org.eclipse.emf.ecore.resource.Resource
        it.getContents() += libraryFactory.createLibrary
    }
    saveResource {
        // it is of type org.eclipse.emf.ecore.resource.Resource
        it.save(null)
        return true
    }
}

...

Resource Save Strategy

Resource saving is delegated to ResourceSaveStrategy (src) which, by defaults only saves the passed Resource (src), by delegating to ResourceManager (src) (see section ResourceManager). You can inject your own save strategy and customize the saving strategy, for instance, you may want to validate the resource before saving (a usable example of this strategy is ValidateBeforeSaveStrategy (src), see also section Validation).

Configurator

In Parsley, instead of using abstract classes, we often provide concrete classes that implement superclass' abstract methods (or interface methods) by delegating to an injected Configurator (src). Such configurator calls methods in its hierarchy using polymorphic dispatch; in particular, the first argument passed to these methods is the object requesting that specific service to the configurator; typically it will be a UI object, e.g., a view part.

These are the methods that can be customized declaratively:

/**
 * Returns the {@link URI} of the resource for the requestor for any use the requestor may need it
 * @param requestor
 * @return
 */

public URI resourceURI(Object requestor) {
    return null;
}

/**
 * Returns the {@link EClass} for the requestor
 * @param requestor
 * @return
 */

public EClass eClass(Object requestor) {
    return null;
}

The idea is that clients that use such an injected instance should call the get methods, e.g., getEClass, while the customization should be defined using polymorphic dispatch, e.g.,

class MyConfigurator extends Configurator {

    public EClass eClass(MyView1 view1) {
        return ...;
    }

    public EClass eClass(MyOtherView view) {
        return ...;
    }
}

In the DSL, you can specify a configurator section, e.g., (the requestor object can be accessed using the implicit variable it):

module my.project {

    configurator {
        resourceURI {
            MyTreeFormView -> {
                return ...;
            }
            MyTableView -> {
                return ...;
            }
        }
        eClass {
            MyTableView -> {
                return ...;
            }
            MyTableFormView -> {
                return ...;
            }
        }
    }
}

The project wizard will generate in the module.parsley the required configurator sections, depending on the specific template chosen, with some // TODO comments to help implementing them, e.g.,

module my.project {

    configurator {
        eClass {
            MyView -> {
                // TODO return the EClass of objects to be shown
            }
        }
        resourceURI {
            MyView -> {
                // TODO create and return a org.eclipse.emf.common.util.URI
                return null;
            }
        }
    }
}

Validation

EMF Parsley supports standard EMF validation automatically, e.g., via the context menu "Validate"; thus, if you already have constraints implemented for your meta-model, the validation action will check them.

EMF validation can also be triggered manually using an injected ValidationRunner (src), which provides methods for validating a single EObject (src) or an entire Resource (src). These validate methods return an EMF Diagnostic (src) that can be used to find out possible errors, warnings and infos collected during the validation.

There are overloaded versions of validate methods that also take an IssueReporter (src):

/**
 * Validates, reports diagnostics through the passed {@link IssueReporter}
 * and returns the list of reported diagnostics.
 * 
 * @param eObject
 * @param reporter
 * @return
 */

public List<Diagnostic> validate(EObject eObject, IssueReporter reporter) {
    return reporter.report(validate(eObject));
}

/**
 * Validates, reports diagnostics through the passed {@link IssueReporter}
 * and returns the list of reported diagnostics.
 * 
 * @param resource
 * @param reporter
 * @return
 */

public List<Diagnostic> validate(Resource resource, IssueReporter reporter) {
    return reporter.report(validate(resource));
}

The reporter is asked to report the collected diagnostic and it is expected to return the list of issues effectively reported. For example, an issue reporter can report only errors (e.g., diagnostic whose severity is Diagnostic.ERROR), while ignoring warnings and other diagnostic information.

We provide a utility class that can be injected, DiagnosticUtil (src), with utility methods, like flattening diagnostic into a list (EMF diagnostic are typically nested in a tree form), to quickly select only the errors, and to have a string representation.

The default implementation of IssueReporter (src) is DialogErrorReporter (src), which uses an EMF dialog to report ONLY errors. Another implementation that can be used for testing purposes is LogIssueReporter (src), which logs diagnostic using the corresponding log4j methods (i.e., error, warn, info).

An example of use of the above classes can be found in ValidateBeforeSaveStrategy (src) (see section Resource Save Strategy):

public class ValidateBeforeSaveStrategy extends ResourceSaveStrategy {

    @Inject
    private ValidationRunner validationRunner;
    
    @Inject
    private IssueReporter issueReporter;

    @Override
    public boolean save(Resource resource) throws IOException {
        if (!precondition(resource)) {
            return false;
        }
        return super.save(resource);
    }
    
    protected boolean precondition(Resource resource) {
        return validationRunner.validate(resource, issueReporter).size() == 0;
    }
}

Thus, if you use a ValidateBeforeSaveStrategy (src), with the default Guice bindings, upon saving, if validation finds errors, it will cancel the saving and it will show a dialog with errors.

Validation is also automatically triggered when editing object's properties in a form or in a dialog. The editing field will be decorated with an error and a tooltip with the error message. Here's an example based on the Library model.

Please keep in mind that for forms and dialogs the error decorations are based on specific features of the object being edited and validated. If you have a custom EMF validator you need to make sure to specify the EStructuralFeature (src) when creating a diagnostic error.