Overview

EMF Parsley is a lightweight framework that allows easy and quick UI development based upon EMF. EMF Parsley is built on top of the EMF Edit framework and it implements features like Trees, Forms and Table builders with standard JFace databinding, providing a complete component-based toolset. EMF Parsley can be configured to use all kinds of EMF persistence implementations (XMI, Teneo, CDO) Moreover a DSL allows to easily customize several behaviors in each component.


Parsley Components

EMF Parsley aims to provide a complete set of components to visualize your model with the introspective EMF capabilities and can be used to easily build forms, viewers or editors.

There are some components that can be used out-of-the-box and can be considered as a reference implementation of the mechanisms that are the basis of EMF Parsley itslef.

  • Trees
  • Forms
  • Tables
  • Editors
  • Search boxes coming soon


Customize

The main feature of EMF Parsley is that you can customize all basic UI behaviours of the components with Dependency Injection mechanisms (based on Google Guice). You can get more info in the Customizations Section, but you don't have to know all details about the internal implementation to inject your own customization because EMF Parsley provides a DSL to easy customize your UI, as explained in the next section.


Customize with the DSL

You can use the DSL by creating a new project with the wizard "Create a new project" -> "EMF Parsley DSL Based project"

Clicking the "Finish" button the wizard will open directly the DSL editor. You can use the content assistant to discover all features.

The DSL allows to customize the most relevant behaviors, currently the following are permitted:

  • parts lets you define your View Parts: a file plugin.xml_emfparsley_gen will be generated that is meant to be synchronized with your own plugin.xml
  • bindings section lets you define which implementation will be used with Injection
  • menu section lets you define the contextual menu for all viewers (trees and tables)
  • features provider is used to retrieve the list of feature for a given EClass to build tables and forms
  • viewer content provider mediates between the viewer's model and the viewer, to provide the contents to be shown
  • Label Provider is used to retrieve the image and text rapresentation of the objects of a tree viewer
  • Caption Provider provides captions for each feature (namely, EStructuredFeature) of the object shown in a form, in a dialog or in a table row. Different implementations can be defined for dialogs, forms and tables.
  • Control Factory provides a custom implementation of the Controls for each feature shown in a form or a dialog. Different implementations can be defined forfor dialogs and forms


First Example

The purpose of this first example is to make use of the classical EMF Library Model example and create a view for editing such models using an EMF Parsley enabled plug-in. We will use one of the saveable views shipped with Parsley: a Tree Form View.

So let's start by creating the model plug-in with

  1. File -> New... -> Example...
  2. from Category "Eclipse Modeling Framework", select "Extended Library Model Example"
  3. press Next and Finish

You will end up with three plug-ins:

  • org.eclipse.emf.examples.library (the model plug-in)
  • org.eclipse.emf.examples.library.edit (the edit plug-in)
  • org.eclipse.emf.examples.library.editor (the editor plug-in)

The editor plug-in project can be removed.

Please consider that here we are starting from this well known EMF model taken out-of-the-box from Eclipse, but you can start from your EMF model (in that case you may probably omit the ".edit" and ".editor" plugins, depending on your model).

Now you can create your first example with the appropriate wizard.

  1. select "File" -> "New" -> "Project..."
  2. from the "Emf Parsley" category select "Emf Parsley Dsl based Project"
  3. click "Next"
  4. give a name to the project (e.g. "org.eclipse.emf.parsley.examples.firstexample") and make sure the checkbox about using one of the templates is checked
  5. click "Next"
  6. Select "Saveable Tree Form View"
  7. click "Finish"

The generated project has some classes and a module.parlsey file, which opens automatically:

			import org.eclipse.emf.parsley.examples.firstexample.FirstexampleSaveableTreeFormView
			
			/* org.eclipse.emf.parsley.examples.firstexample Emf Parsley Dsl Module file */
			module org.eclipse.emf.parsley.examples.firstexample {
				
				parts {
					viewpart org.eclipse.emf.parsley.examples.firstexample {
						viewname "Firstexample"
						viewclass FirstexampleSaveableTreeFormView
					}
				}
				
				configurator {
					resourceURI {
						FirstexampleSaveableTreeFormView -> {
							// TODO create and return a org.eclipse.emf.common.util.URI
							return null;
						}
					}
				}
				
				resourceManager {
					initializeResource {
						// Optional: initialize an empty Resource
						// 'it' is of type Resource
						// e.g., it.getContents += myFactory.createMyClass
					}
				}
			}
			

The viewpart corresponds to the standard Eclipse view part extension point; the Parsley DSL will generate a plugin.xml_emfparsley_gen into the root folder of your project. Just copy this file into "plugin.xml". Parsley will never override your plugin.xml file; each time you modify the module file, the plugin.xml_emfparsley_gen will be generated: it is up to you to keep the generated file synchronized with your "plugin.xml". The easiest way is to select to the files, and use the context menu "Compare With" => "Each Other".

The wizard will also generate a view part class into the project (in this example, FirstexampleSaveableTreeFormView); you can add other controls into that view, or customize other behaviors. Note that the Parsley DSL will never touch the files into the src source folder. On the contrary, files generated into emfparsley-gen source folder must never be manually modified, since its contents will be regenerated each time you modify the module.parsley file.

For example, let's change the view name into "My Library Tree Form":

			...
			module org.eclipse.emf.parsley.examples.firstexample {
				
				parts {
					viewpart org.eclipse.emf.parsley.examples.firstexample {
						viewname "My Library Tree Form"
						...
			

Let's save the file, wait for Eclipse to rebuild, and update the plugin.xml with the new plugin.xml_emfparsley_gen.

(Other viewpart sections can be created; content assist is available for that).

In the generated module.parsley, there is a configurator section, with a TODO comment (The Configurator is detailed in the section Configurator):

			configurator {
				resourceURI {
					FirstexampleSaveableTreeFormView -> {
						// TODO create and return a org.eclipse.emf.common.util.URI
						return null;
					}
				}
			}
			

Let's focus on the above resourceURI: our goal is allowing to manage a library model instance which persists on a EMF Resource . So we must specify the URI of the resource that will be edited by our tree form view. In this example we choose to use the EMF default persistence (XMI), but you can provide any URI (e.g. using Teneo, CDO or any other EMF Resource Persistence implementation) In particular here we choose to persist the Resource in a XMI file named "MyLibrary.library" into the user home folder (you might want to change it with any other path). To achieve this, we just need to create such a URI (recall that content assist is available when typing Xbase expressions):

			configurator {
				resourceURI {
					FirstexampleSaveableTreeFormView -> {
						return URI.createFileURI( System.getProperty("user.home") + "/MyLibrary.library" );
					}
				}
			}
			

If you simply copy and paste the above return statement, you'll get an error about unresolvable Java type URI; "Organize Imports" context menu (or its shortcut "Ctrl+Shift+O") can be used to automatically add the missing import (make sure you select URI ).

Note that the specified URI, will be used for loading the resource only for our specific view (the resource will be automatically created if it does not exist).

In the module.parsley there is another section that has been generated by the wizard:

			resourceManager {
				initializeResource {
					// Optional: initialize an empty Resource
					// 'it' is of type Resource
					// e.g., it.getContents += myFactory.createMyClass
				}
			}
			

We can use this section to initialize our resource when it is empty (i.e., the first time the resource is created); (The resourceManager is detailed in section Resource Manager).

In this example, we want to initialize an empty resource with a Library object; so we first need a Dependency from the model plug-in: so open MANIFEST.MF file, go to Dependencies tab, press "Add..." button in "Required Plug-ins" section and insert "org.eclipse.emf.examples.library" among dependencies.

Now we can implement the initializeResource method (as described in the comment, the Resource to initialized is available through the parameter it); the Library object is created using the standard EMF API: we need the factory of the library model:

			resourceManager {
				initializeResource {
					it.getContents += EXTLibraryFactory.eINSTANCE.createLibrary
				}
			}
			

Again, use the content assist while typing, e.g., for automatically importing the type EXTLibraryFactory, or use the "Organize Imports" functionality.

Now, we are ready to execute this example: let's get back to the MANIFEST.MF and run the example

As an Eclipse RCP developer you know, of course, that this will start another Eclipse instance (unless you add an Application plug-in to the launch or define an Application in the current plug-in).

In this second Eclipse instance you can show the View in this way:

  1. Window -> Show View -> Other...
  2. from Category "Other", select "My Library Tree Form"
  3. press OK

With this simple view you can start editing the model instance. For example you can set the "name" field; as soon as you start typing characters into this field you will notice that:

  1. the View turns to a "dirty" state (an asterisk symbol appears on the view tab)
  2. the "Save" toolbar button is enabled
  3. the typed characters are reflected into the label correspondent to the Library icon

if you now perform a "Save" action the persistence mechanism will trigger and you will see that file

<user.home>/MyLibrary.library

is being created on the file system. From now on, this file will keep the state of the model object whenever you change and save it.

To create a Writer into the Library just right-click on the Library object and select New Child -> Writer

Please note that you might see a slightly different content in the above context-menu in case you deleted the .edit plugin when creating the model (e.g. "Writers Writer" instead of "Writer", "Stock Book" instead of "Book" and similar (this is because with EMF it is possible to customize labels also via .edit plugin).

Now set for instance the writer "name" field and save. Now just play around creating Books, associating them to Writers and so on. As you can see you can entirely manage the EMF model instance: creating, modifying and deleting elements.

Whenever the current selection on the upper side of the view changes, then the lower side shows the detail of this selection.

However, up to this point, you have no control over the field to be shown and its order; for example you may want just the "name" attribute for the Library and "name", "address" and "books" attributes for Writers and maybe "title", "authors" and "category" for Books.

Well, it's indeed very easy to obtain this: just edit the module.parsley file, adding the following import (without ending line with ";")

			import org.eclipse.emf.examples.extlibrary.*
			

and then defining the features to show (the featuresProvider is detailed in section Features Provider):

			module ... {
			
			...
				featuresProvider {
			        features {
			            Library -> name
			            Writer -> name, address, books
			            Book -> author, title, category
			        }
			    }
			}
			

Remeber that code completion is available, just exploit it since it helps a lot.

If you restart now the application you will see that, when selecting an object, only the features specified in the above section will be shown for each specified classes. Furthermore, they are shown in the specified order.

NOTE: Did you run the application in Debug mode? Well, then you can change fields and order, save and see the changes without even restarting the application.

Do you want to change text used for attribute captions in the form for a specific class? Just add the following (featureCaptionProvider is detailed in section Feature Caption Provider):

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

Or do you want to change the label shown on the tree nodes on the upper side and as detail title? Maybe want to format the book label like this? (labelProvider is detailed in section Viewer Label Provider):

			...
			labelProvider {
			    text {
			        Book b -> { '"' + b.title + '"' }
			        Writer w -> { w.name }
			    }
			}
			

The result of all the above customizations is shown in the following screenshot (compare it with the previous screenshot):

Now, let's customize the context menus; by default, Parsley will generate context menus using EMF.Edit:

We will now customize the context menu for books and writers, using the menuBuilder in the DSL (context menu customization is detailed in section Contextual Menu). What we want to achieve is to have a context menu for a Writer to add a new book in the library, and set its author to the selected writer (similarly, we want a context menu for a Book to add a new writer in the library, and set the selected book as one of the new writer's books):

			...
			menuBuilder {
				val factory = EXTLibraryFactory.eINSTANCE
				
				emfMenus {
					Writer w -> #[
						actionAdd(
							"New book",
							(w.eContainer as Library).books,
							factory.createBook,
							[ book |
								book.title = "A new book" 
								book.author = w
							]
						)
					]
					Book b -> #[
						actionAdd(
							"New writer",
							(b.eContainer as Library).writers,
							factory.createWriter,
							[ writer |
								writer.name = "A new writer"
								writer.books += b
							]
						)
					]
				}
			}
			

In this code we use Xbase features like list literals (#[...]) and lambda expressions.

If you now restart the application, you see that the new context menu appears on writer elements:

And selecting such a menu on a writer will add a new book, with a title, and whose author is the selected writer:

You may want to try the new context menu on a book as well.

Now, let's customize the contents shown in the tree view: by default, as you can see from the previous screenshots, the tree will show all the contents of the library. If we want to show only the writers and the books we can specify this section in the DSL (the customization of the content provider is detailed in section Viewer Content Provider):

			...
			viewerContentProvider {
				children {
					Library -> {
						writers + books
					}
				}
			}
			

and the result can be seen in the following screenshot:

By default, double-clicking on a tree viewer of a saveable view will show a dialog to edit that object (if you customized the featuresProvider, the dialog will use your customized version); by default, if you edit a field in such dialog, the modifications will be applied immediately to the resource: this can be seen in the labels of the tree which are automatically updated and in the dirty state of the view:

Such a strategy for editing is delegated to an injected IEditingStrategy , which is implemented by default by OnTheFlyEditingStrategy .

One may want to avoid this automatic update of the resource, and have the changes applied only when the "OK" dialog button is pressed (if "Cancel" is pressed, no changes should be applied at all). To achieve this behavior, it is enough to bind the alternative implementation UndoableEditingStrategy , in the Guice module. This can be achieved in the DSL using the binding section (Guice bindings are detailed in section Guice Bindings):

			...
			bindings {
				type IEditingStrategy -> UndoableEditingStrategy
			}
			

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

Besides types, you can also bind (i.e., inject) specific values that are used in the framework; for example, you can change the orientation of the tree form sash as follows

			...
			bindings {
				type IEditingStrategy -> UndoableEditingStrategy
				value int TreeFormSashStyle -> SWT.HORIZONTAL
			}
			

and see the result:

This ends the first tutorial.


Components

This section describes the components that are provided to be used out-of-the-box, that are the foundations for all parts built upon EMF Parsley. Afetr a brief description, for each component we present a set of customizations, just to give an idea of how it works. You can refer to Customizations Section for a complete list.


Form Component

The Form Component can be used to rapresent an EObject in a form, like in the image above.

EMF Parsley provides a factory that can be used to create such a component, like in the code below. Here you can see that a form can be configured in 2 lines, the constructor phase and the build&fill phase.

	@Inject FormFactory formFactory;
	
	(...)
	
	formComposite = formFactory.createFormDetailComposite(parent, SWT.NONE);
	formComposite.init(eObject);
	

Most of the job is done by the second line of code, which gets the list of EStructuralFeature defined for the EClass (that is the type of the object to represent) and builds a row for each of them. Each row is composed by a caption which defaults to the name of the feature and a control to access the data.

All these aspects can be customized in many ways, for example you can customize the feature list, the captions and the controls.


Feature List Customization

The list of features displayed on the form can be customized via the Feature Provider that returns the list of the features (in a given order).


Caption Customization

The captions of the features shown in the form can be customizzed via the Form Feature Caption Provider.


Control Customization

The Controls in the form can be customized via the Form Control Factory.


Proposal Provider

Depending on the feature types, some fields can have predefined values (e.g. combo). You can provide the exact proposal list via the Proposal Provider


Tree Component

The Tree Component provides a tree rapresentation of data that can be fed with an EResource, a Resource URI, and a simple EObject. This component uses the EMF Meta-Model information to display objects in the tree.

EMF Parsley provides an initializer that can be used to create such a component, like in the code below. Here you can see that can be configured only in 2 lines, the constructor phase and the build&fill phase.

	@Inject ViewerInitializer viewerInitializer;
	
	(...)
	
	treeViewer = new TreeViewer(parent);
	viewerInitializer.initialize(treeViewer, element);
	

The Tree Componentcan be customized in several way via the standard EMF Edit facilities or with the EMF Parsley codeinjection based mechanism. If you are familiar with Jface APIs, you can easily use the basic class with some facilties to define the specific implementation. See the corrisponding sections for more details.


Content Provider

An IContentProvider is used in Jface to retrieve the list of elements and children to be showed in the tree viewer. The Viewer Content Provider is the EMF Parsley implementation of that interface, and by default uses the containment mechanisms to provide children as in EMF Edit framework, but it can be customized as weel.


LabelProvider

The Viewer Label Provider is the implementation of an ILabelProvider interface and is responsible to provide the text and image rapresentation for each EObject visualized.


Adding Menu

The contextual menu can be added to the viewer via the ViewerInitializer, as explained in the Menu section. The Menu Builder allows to fully customize the menus.


Tree Form Component

The Tree Form Component contains a that provides a tree rapresentation of data that can be fed with an EResource, a Resource URI, and a simple EObject. This component uses the EMF Meta-Model information to display objects in the tree. The component also combines a detail that display the current selected object.

EMF Parsley provides a factory to create such a component.

	@Inject TreeFormFactory treeFormFactory;
	
	(...)
	
	treeFormComposite = treeFormFactory.createTreeFormComposite(parent, SWT.BORDER);
	treeFormComposite.update(application);
	

Since Tree Form Component is a combination of Tree Component and Form Component, all their customizations are avaible for it.


Table Component

The Table Component can rapresent data in a grid, once you have specified the type of objects to represent. It uses metamodel information to build columns as needed.

	@Inject ViewerFactory viewerFactory;
	
	(...)
	
	tableViewer = viewerFactory.createTableViewer(composite,SWT.BORDER | SWT.FULL_SELECTION, object, eClass);
	

The class TableViewerColumnBuilder has the responsibility to build the columns of the Table, by using the Features Provider to get the list of features and the Feature Caption Provider for the column headers. The class TableColumnLabelProvider can be used to specify an implementation of ILabelProvider for each cell in the table.


Feature List Customization

The list fo features displayed on the table can be customized via the Table Feature Provider. This list of features will be used for building the columns of the table.


Caption Customization

The headers of the table can be customizzed via the Caption Provider.


Adding Menu

The context menu can be added to the viewer via the ViewerInitializer, as explained in the Menu section The Menu Builder allows to fully customize the menus.


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 that is suitable to be used in an OSGI environment, like Eclipse itself or RAP (see also EMF Parsley RAP support). 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 (this is the base class of EmfParsleyGuiceModule ). 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).


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 , 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 (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 Prorvider 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.

		labelProvider{
			text{
				Book -> "Book:"+title
				Borrower -> "Borrower: "+firstName
			}
			image{
				Book -> "book.png"
			}
		}
		

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 (TODO). EMF Parsley provides such a base implementation with the class ViewerLabelProvider that is meant to be subclassed by the programmer to provide specific implementations like in the example below.

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


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 s of a given EClass it relies on an injected FeaturesProvider . 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 .

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 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 and for the list of EStructuralFeature ; note that the name of the EClass should be obtained by using getInstanceClassName(); you can also combine the two approaches.


Table Features Provider

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

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


Feature Caption Provider

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

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

If you want to customize it in Java, you need to derive from FeatureCaptionProvider . It can be customized, with injection TODO (see Injection paragraph), to customize the caption label on the left of each control in a form 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 "Wrote by:";
		}
		
		public String text_Writer_name(final EStructuralFeature feature) {
			return "Name:";
		}
		


Form Feature Caption Provider

The FormFeatureCaptionProvider can be used if you want to define the description only for the form. 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 control, 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;
		}
		

If there is no customization in the FormFeatureCaptionProvider we fall back to FeatureCaptionProvider .


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 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 viewer by using the ViewerInitializer .

	@Inject ViewerInitializer viewerInitializer;
	(...)
	
	treeActionBarContributor.initialize(editingDomain);
	viewerInitializer.addContextMenu(treeFormComposite.getViewer(),
	treeActionBarContributor, editingDomain, this);
	treeFormComposite.getViewer().addSelectionChangedListener(treeActionBarContributor);
	

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

IMPORTANT: do not initialize any reference feature of the created EObject upon object creation; 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,
					// WRONG: don't do that
					EXTLibraryFactory.eINSTANCE.createBook => [
						author = w
					]
				)
			]
		}
		

Since the object is created when the contextual menu is created, NOT when the menu is selected; if you do something like in the code above, you will end up with a resource with dangling references (which cannot be saved).

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,
					// CORRECT: the lambda will be executed when the menu
					// is selected and the object has been added to
					// the resource
					[ book | book.author = w ]
				)
			]
		}
		


Factories


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 . Using the same polimorphic mechanism of the labels, the programmer can write a method with the keyword 'control' followed by the EClass and EStructuralFeature undescore-character-separated. The method must accept as parameters the DataBinding Context and the Feature Observable that can be used for databinding.

		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.


Resources

  • If you need a machanism to fill some data for the first time you use a model, you can provide a specific implementation of Resource Manager.
  • If you want to interact with Resource Loading, you can provide a specific Resource Loader

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


Resource Loader

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


Resource Manager

Tasks concerning an EMF Resource are delegated to ResourceManager .

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 which, by defaults only saves the passed Resource , by delegating to ResourceManager (see ). 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 , 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 . 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;
	}
	
	/**
	 * Returns the {@link EStructuralFeature} for the requestor
	 * @param requestor
	 * @return
	 */
	public EStructuralFeature eStructuralFeature(Object requestor) {
		return null;
	}
	
	/**
	 * Returns the contents from the resource for the requestor
	 * @param requestor
	 * @param resource
	 * @return
	 */
	public Object contents(Object requestor, Resource resource) {
		return null;
	}
	

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

	class MyConfigurator extends Configurator {
	
		public Object contents(MyView1 view1, Resource resource) {
			return ...;
		}
	
		public Object contents(MyOtherView view, Resource resource) {
			return ...;
		}
	}
	

In the DSL, you can specify a configurator section, e.g., (the requestor object can be accessed using the implicit variable it, while additional parameters can be accessed through their names, as named in the original Configurator class, e.g., resource for contents):

	module my.project {
	
		configurator {
			resourceURI {
				MyTreeFormView -> {
					return ...;
				}
			}
			eClass {
				MyTreeView -> {
					return ...;
				}
			}
			eStructuralFeature {
				MyTreeFormView -> {
					return ...;
				}
				MyFormView -> {
					return ...;
				}
			}
			contents {
				MyTreeView -> {
					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 {
			contents {
				MyView -> {
					// TODO return the contents from the resource
					// e.g., resource.^contents.get(0)
				}
			}
			eClass {
				MyView -> {
					// TODO return the EClass to represent
				}
			}
			resourceURI {
				MyView -> {
					// TODO create and return a org.eclipse.emf.common.util.URI
					return null;
				}
			}
			eStructuralFeature {
				MyView -> {
					// TODO return the EStructuralFeature to get the elements to represent
				}
			}
		}
	}
	


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 , which provides methods for validating a single EObject or an entire Resource . These validate methods return an EMF Diagnostic 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 :

	/**
	 * 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 , 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 is DialogErrorReporter , which uses an EMF dialog to report ONLY errors. Another implementation that can be used for testing purposes is LogIssueReporter , 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 (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 , with the default Guice bindings, upon saving, if validation finds errors, it will cancel the saving and it will show a dialog with errors.


Advanced Features

In this chapter we describe some advanced features.


Testing Framework

We provide some utility classes for testing EMF Parsley components in the feature "Emf Parsley Junit4 Support". By deriving from one of the abstract classes in our testing bundle, you will be able to write tests that are meant to be run as Junit test, that is to say, NOT as Plug-in Junit tests. Thus, you will not need a running Eclipse product to execute such tests: they will be much faster. Indeed, many parts of Parsley can be tested even without a running Eclipse.

  • AbstractEmfParsleyTest : this provides a few utility methods, e.g., for creating an Injector .
  • AbstractEmfParsleyShellBasedTest : this allows to run Junit tests that require a Display and a Shell .
  • AbstractEmfParsleyControlBasedTest : an extension of the previous class for tests that also require databinding capabilities, e.g., tests for Control elements; this provides many assert methods for several kinds of controls, such as assertCheckbox, assertCombo, etc.

We use these classes for testing most of our classes; you might want to have a look at the project org.eclipse.emf.parsley.tests for some usage examples.


RAP


Introduction

As you may know RAP (Remote Application Platform) is a technology that allows you to run an Eclipse RCP application over the web.

In order to obtain this goal you have to setup a specific RAP Target Platform, for instance the one that RAP itself provides once you install it.

However when you want to use an Eclipse RCP framework over the RAP Platform, you generally have to deal with dependencies, since not all Eclipse frameworks are ready-to-use with RAP, especially those related with the SWT layer.

EMF Parsley provides a proper RAP Target Platform that allows you to start leveraging Parsley potentials to the web the same way you have learned to do with desktop (RCP) development.


Setup


Installing the RAP Tools

To begin with, you need to install the RAP Tools into the IDE. This can be accomplished with the following steps:

  1. Help -> Install New Software ...
  2. select the main Eclipse Update site (e.g. "http://download.eclipse.org/releases/luna")
  3. expand category "Web, XML, Java EE and OSGi Enterprise Development"
  4. select "RAP Tools" and complete the installation, restarting the IDE at the end
  5. after IDE restarts just close the Welcome page


Setup the EMF Parsley RAP Target Platform

After having installed EMF Parsley as described here and created a new workspace, you can setup the EMF Parsley RAP Target Platform in the following way:

  1. File -> New... -> Example...
  2. from Category "Emf Parsley Examples", select "Emf Parsley Rap Target Platform Example"
  3. press Next and Finish
  4. open the Target Definition file emf-parsely-rap-2.3.target
  5. wait until the "Resolving Target Definition" job is done (check the status bar)
  6. when finished, click on hyperlink "Set as Target Platform"

You will end up with a RAP-enabled workspace, enhanced by EMF and Parsley!


Examples


Running the Parsley RAP UI Example

Here is the fastest way to get a working web application with all the stuff put togheter:

  1. File -> New... -> Example...
  2. from Category "Emf Parsley Examples", select "Emf Parsley Rap Example"
  3. press Next and Finish
  4. expand plug-in "org.eclipse.emf.parsley.examples.rap.ui"
  5. right-click "Emf_Parsley_RAP_UI_Example.launch" and click "Run as" "Emf_Parsley_RAP_UI_Example"

What you will get is a web application that allows you to interact with the model instance as you would do in a desktop (RCP) environment.

In this web application you can see two views:

  • the one on the left is a read-only view; it just reflects the model content, but it does not react to changes (the classic Eclipse dirty indicator is not triggered by changes) and you are not able to save. Its model is created in class org.eclipse.emf.parsley.examples.rap.ui.GuiceModule.CustomEmptyResourceInitializer and is not persisted
  • the view on the right is instead a Saveable view and therefore it not only triggers the dirty state after a change, but also allows you to save the modifications with the automatic dirty state reset. Its model is persisted in file System.getProperty("java.io.tmpdir")+"/My.model")

Of course, since this is a web application, you can also open a browser on another pc or device on the same network and type the address, replacing 127.0.0.1 with the IP of the machine where the application was launched.


Running the Parsley RAP CDO Example

The EMF default XMI persistence is certainly very handy to start with, but as soon as you want a more production-ready EMF persistence architecture, well, CDO is for sure the way to go. In fact with CDO you basically have an EMF model instance shared between clients, that also allows the clients to be synchronized with the model changes.

In this example, in order to keep things simple, we will use CDO with an in-memory store (MEMStore) whose contents will be lost once the server is stopped. However CDO can be configured for usage with RDBMS, Object-oriented or NO-SQL databases (see here for details)

To start with we need a CDO Server running and we can obtain it with an example plugin that can be used both in an RCP and in a RAP workspace.

  1. File -> New... -> Example...
  2. from Category "Emf Parsley Examples", select "Emf Parsley Cdo Server Example"
  3. press Next and Finish
  4. expand plug-in "org.eclipse.emf.parsley.examples.cdo.server"
  5. right-click "CDOServerExample.launch" and click "Run as" "CDOServerExample"
  6. a message on the Console "Repository[demo] started!" informs that the CDO Server instance is started!

Now we can create the web application that will use the CDO server just started.

  1. File -> New... -> Example...
  2. from Category "Emf Parsley Examples", select "Emf Parsley Rap Cdo Example"
  3. press Next and Finish

The plug-in projects created are:

  • the Model (org.eclipse.emf.parsley.examples.cdo.model)
  • a Parsley plug-in with a TreeForm (org.eclipse.emf.parsley.examples.cdo.treeform)
  • the webapp (org.eclipse.emf.parsley.examples.cdo.rap)

Then let's start the application

  1. expand plug-in "org.eclipse.emf.parsley.examples.cdo.rap"
  2. right-click "EMF-Parsley_Library_RAP.launch" and click "Run as" "EMF-Parsley_Library_RAP"

If you happen to see this

just press the refresh button and should see the following

Now feel free to open the same address from more browsers window (yes, on different machines or devices, possibly) and see the power of this technology stack at work!


Eclipse 4.x

Instead of using the Extension Point mechanism, EMF Parsley leverages from DSL and Google Guice Injection.

Because of this, it is very easy to use it with Eclipse 4.x (e4).


First Example Setup

If you followed the steps described in section First Example you will have already what we need to begin. Otherwise the following wizard will bring you to that point.

  1. File -> New... -> Example...
  2. from Category "Emf Parsley Examples", select "Emf Parsley First Example"
  3. press Next and Finish

You will end up with three plug-ins:

  • org.eclipse.emf.parsley.examples.firstexample (the EMF Parsley example plug-in)
  • org.eclipse.emf.examples.library (the model plug-in)
  • org.eclipse.emf.examples.library.edit (the model.edit plug-in)

As a reminder, in section First Example we reached the point where we launched a second Eclipse instance (but, of course, just defining a product you could have a standalone 3.x application) with a view (called "My Library Tree Form") that allowed to manage the model.


Preparing for a pure e4 Application

What we will do now is starting from the previous step and create an e4 Application (on top of the previous plug-ins) that gets to the same result, but now with a pure e4 Part.

In order to do this we need to export the "org.eclipse.emf.parsley.examples.firstexample" package from the first plug-in.


Create an e4 Application

Now let's create a new, empty, e4 application, e.g. "org.eclipse.emf.parsley.examples.firstexample.application" (you can find details on how to create e4 applications in our tutorials).

Create a Part and ensure that the application starts.


Using a TreeComposite into an e4 Part

In the just created plug-in we need dependencies from the previous plug-ins: so open the org.eclipse.emf.parsley.examples.firstexample.application/MANIFEST.MF file, go to Dependencies tab and add the three previous plug-ins. Add also "org.eclipse.emf.parsley" plug-in. Don't forget to add the previous, and the required plug-ins, also to the Product.

Open the Part java class and make the following changes:

	// Use these imports during Organizing Imports operation
	import org.eclipse.emf.common.util.URI;
	import org.eclipse.emf.ecore.resource.Resource;
	import org.eclipse.swt.widgets.Composite;
	
	// The part implements IMenuListener for context menu handling
	public class MyEclipse4Part implements IMenuListener
	
	//the parent composite
	private Composite parent;
	//the EMF Parley composite for showing a tree and a detail form
	private TreeFormComposite treeFormComposite;
	//the EMF Resource
	private Resource resource;
	
	//Guice injected EMF Parsley component for contributing to the tree context menu
	private TreeActionBarContributor treeActionBarContributor = FirstexampleActivator.getDefault().getInjector()
			.getInstance(TreeActionBarContributor.class);
	
	
	//Guice injected EMF Parsley factory for the tree detail form
	private TreeFormFactory treeFormFactory = FirstexampleActivator.getDefault().getInjector()
			.getInstance(TreeFormFactory.class);
	
	//Guice injected EMF Parsley Resource loader
	private ResourceLoader resourceLoader = FirstexampleActivator.getDefault().getInjector()
			.getInstance(ResourceLoader.class);
	
	//Guice injected EMF Parsley editing domain
	private AdapterFactoryEditingDomain editingDomain = FirstexampleActivator.getDefault().getInjector()
			.getInstance(AdapterFactoryEditingDomain.class);
	
	//Guice injected viewer initializer
	private ViewerInitializer viewerInitializer = (ViewerInitializer) FirstexampleActivator.getDefault().getInjector()
			.getInstance(ViewerInitializer.class);
	
	//Guice injected save manager
	private ResourceSaveManager resourceSaveManager = FirstexampleActivator.getDefault().getInjector()
			.getInstance(ResourceSaveManager.class);
	
	//URI for EMF Resource
	private URI uri = URI.createFileURI(System.getProperty("user.home")
			+ "/MyLibrary.library");
	

Modify the @PostConstruct method with this code:

	@PostConstruct
	public void postConstruct(Composite parent) {
		this.parent = parent;
	
		// Initialize TreeFormFactory & ResourceLoader
		init(treeFormFactory, resourceLoader);
	
		// Prepare the menu action bar contributor upon the selection
		treeFormComposite.getViewer().addSelectionChangedListener(treeActionBarContributor);
	}
	

and add the following methods:

	public void init(TreeFormFactory treeFormFactory, ResourceLoader resourceLoader) {
		//create the tree-form composite
		treeFormComposite = treeFormFactory.createTreeFormMasterDetailComposite(parent, SWT.BORDER);
		//load the resource
		resource = resourceLoader.getResource(editingDomain, uri).getResource();
		//update the composite
		treeFormComposite.update(resource);
		//initialize and bind the context menu to the tree-form composite
		treeActionBarContributor.initialize(editingDomain);
		viewerInitializer.addContextMenu(
				treeFormComposite.getViewer(), treeActionBarContributor, editingDomain, this);
	}
	
	@Override
	public void menuAboutToShow(IMenuManager manager) {
		treeActionBarContributor.menuAboutToShow(manager);
	}
	

If you now run the application you will be able to manage the model:

but you will notice that it is not possible to persist the changes to the model.


Adding the dirty state and Save command

In order to allow persisting the model changes we have to add the dirty state handling to the part and the Save command to the application. Let's start with adding the following attribute to the part

	@Inject
	MDirtyable dirtyable;
	

initialize it in the @PostConstruct method

	@PostConstruct
	public void postConstruct(Composite parent, MDirtyable dirtyable) {
	
			this.dirtyable = dirtyable;
	
			this.dirtyable.setDirty(false);
	

add to init method the following code in order to update the dirty state

	editingDomain.getCommandStack().addCommandStackListener(
				new CommandStackListener() {
					public void commandStackChanged(EventObject event) {
						if (dirtyable != null)
							dirtyable.setDirty(true);
					}
				});
	

and add the @Persist method, which will be called when the part is saved

	@Persist
	public void save(MDirtyable dirty) throws IOException {
		if (resourceSaveManager.save(resource)) {
			if (dirty != null) {
				dirty.setDirty(false);
			}
		}
	}
	

and, in the end, add the Save handler along with the correspondent Command and Menu (you can find how to create handlers, commands and menus in an e4 applications in our tutorials)

	public class SaveHandler {
	
		@Execute
		void execute(EPartService partService, @Named(IServiceConstants.ACTIVE_PART) MPart part) {
			partService.savePart(part, false);
		}
	}
	


Migration Guide


From 0.3 to 0.4

  • EmptyResourceInitializer has been removed: you should now use ResourceManager , see section Resource Manager
  • ResourceSaveManager has been removed: you should now use ResourceSaveStrategy , see section Resource Save Strategy
  • Project wizards have been redesigned: they generate a module.parsley that uses the configurator for specifying required information (see section Configurator and the updated first example, section First Example)