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.

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")
  5. click "Finish"

The generated project has some classes and a module.parlsey file, which opens automatically. Just type the following content into the {} block (note that context assist is available)

			parts {
				viewpart org.eclipse.emf.parsley.examples.firstexample.view.library.treeform {
					viewname "My Library Tree Form"
					viewclass 
				}
			}
			

Here above we have just declared a view part with

  1. id "org.eclipse.emf.parsley.examples.firstexample.view.library.treeform"
  2. name "Library Tree Form"
  3. class: not yet set, we are going to create "org.eclipse.emf.parsley.examples.firstexample.views.LibraryTreeFormView"

So there is still an error, in fact we need to create the declared class. Suggestion: Just copy the above qualified class name ("org.eclipse.emf.parsley.examples.firstexample.views.LibraryTreeFormView"), select the project, right-click and select File -> New -> Class. Now paste the clipboard content into "Name:" field: the package and class name will be automatically splitted into right fields. Then click "Finish".

Now modify the view class code with this content:

			import org.eclipse.emf.parsley.views.AbstractSaveableTreeFormView;
			import org.eclipse.emf.common.util.URI;
			
			public class LibraryTreeFormView extends AbstractSaveableTreeFormView {
			
				protected URI createResourceURI() {
					return URI.createFileURI( System.getProperty("user.home") + "/MyLibrary.library" );
				}
			
			}
			

Now get back to module.parlsey file, go just after "viewclass" keyword, type "LibraryTreeFormView" and Ctrl-Space: the content assist will set automatically the correct qualified name (it will insert the class name, and add the corresponding import at the beginning of the file):

				viewclass org.eclipse.emf.parsley.examples.firstexample.views.LibraryTreeFormView
			

when you save the above file you will notice that a new file ("plugin.xml_emfparsley_gen") is generated into the root folder of your project. Just copy this file into "plugin.xml".

Let's focus briefly on the above createResourceURI() method: our goal is allowing to manage a library model instance which persists on a EMF Resource. 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).

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

Before executing this code we need to do some little more work in order to initialize the Resource.

An easy way to overcome this issue can be the following:

  1. open the class *GuiceModule (FirstexampleGuiceModule in this example) that the wizard created for you. Note that this is a key class for all customizations; it allows to override several methods in order to provide custom behaviors and implementations.
  2. override method bindEmptyResourceInitializer() providing a class for Resource initialization
    				public Class<? extends EmptyResourceInitializer> bindEmptyResourceInitializer() {
    					return LibraryEmptyResourceInitializer.class;
    				}
    				
  3. create a new class LibraryEmptyResourceInitializer extending from EmptyResourceInitializer for Resource initialization
    				import org.eclipse.emf.parsley.resource.EmptyResourceInitializer;
    				import org.eclipse.emf.ecore.resource.Resource;
    				import org.eclipse.emf.examples.extlibrary.EXTLibraryFactory;
    				import org.eclipse.emf.examples.extlibrary.Library;
    				
    				public class LibraryEmptyResourceInitializer  extends EmptyResourceInitializer { 
    				    
    				   public void initialize(Resource resource) { 
    				       super.initialize(resource); 
    				       Library library = EXTLibraryFactory.eINSTANCE.createLibrary(); 
    				       resource.getContents().add(library); 
    				   }
    				
    				}
    				
    The method initialize() will be executed only when the Resource is empty (i.e., it has no contents).

now 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

			module ... {
			
				parts { 
					...	
				}
				
				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 declared attributes will be shown. Furthermore, they are shown in the specified order.

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 labels in the detail? Just add the following:

			propertyDescriptionProvider {
				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 {
				text {
					Book b -> '"' + b.title + '"' +' (by '+ b.author.name + ')'
				}
			}
			


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.


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 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 -> #[
					actionAdd("Add a new book", books,
						EXTLibraryFactory.eINSTANCE.createBook => [
							title="new book"
						]
					)
				]
			}
		}
		

You can customize menu also via Java, by extending the EditingMenuBuilder .


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.


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.


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!


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