Sash and Core
Architectures

Author : Cedric Dumoulin

Date : 6 dιc. 2009

Rev : 22 Jan. 2010

 

1.   Introduction

Sash System and Core are used by the Papyrus UML editor.

The Sash System provides an editor of pages that can be arranged in multiple pages separated by sashes. A page can be either an editor or a SWT Component.

The Core provides the main Editor implemented with the Sash. The core also provides a notion of Services that can be added to the main editor. The services can then be retrieved and shared between nested pages.

The Core depends on the Sash System (Figure 1). The Sash System is independent.

 

Figure 1 Sash and Core packages

2.   Sash System

The sash system is used to render multiple nested pages in one main editor. A page can be an editor or a SWT component.

In the sash system, nested pages can be layed in tabfolders and side by side. The sash system allows to move the pages from tabfolder to tabfolder, or to create new tabfolders.

2.1.        Architecture Overview

The sash system main component is the SashWindowsContainer class. It is used to render the pages as described above. The class requires two interfaces in order to work properly (Figure 2):

 

Figure 2 Sash Container and its required interfaces

 

The Sash Container can be used directly in an Eclipse editor. A base implementation is provided in AbstractMultiPageSashEditor. This implementation requires a concrete implementation for the ISashWindowsContentProvider interface.

2.2.        ContentProvider

The ContentProvider is used by the SashContainer to discover the content of the sash, and to store this content externally.

The ContentProvider main API is ISashWindowContentProvider.

 

2.2.1.   ContentProvider structure

The ContentProvider is used to read and store the structure to be rendered by the SashContainer. This structure can be implemented in different manners.

The ContentProvider API is design in order to be able to implement the structure by using any kind of Objects, called raw model. The Container call special methods (createChildSashModel(Object)) to obtain a well known API from a raw model used in the container.

The content provider returns a view on the real structure implementation. The SashContainer start to explore this view from the ISashWindowContentProvider. The virtual view is made of ‘Model’ represented by the different IXxxModel interfaces in Figure 3.

Figure 3 ContentProvider set of interfaces used by the SashContener

 

From the content provider, the  virtual view is as follow:

 

2.2.2.   ContentProvider main API

The main API of the content provider is ISashWindowsContentProvider.

There is two methods to explore the ContentProvider structure: getRootModel() and createChildSashModel(object).

Others methods are used by the Container to modify the content after a user interaction.

2.2.3.   ContentProvider exploration

To discover the content, the Container first call the getRootModel() to get the root model of the content. This returns an Object representing the root of the structure of the Sash.

Then the container call createChildSashModel(object) whith the previous object to get the Model represented by the Object. This is either a ISashPanelModel or a ITabFolderModel.

For a ISashPanelModel the container call the getChildren() method to obtain the raw models of the node. Then for each childs it get the IAbstractPanelModel by calling createChildSashModel(object).

For a a ITabFolderModel, the container call the getChildren() method to obtain the raw models of the node. Then for each childs it get the IPageModel by calling createChildSashModel(object). This return either a IComponentModel or a IEditorModel, representing a SWT Component or a IEditor.

The container create the corresponding SWT component or IEditorPart by calling appropriate method.

2.2.4.   ContentProvider Modification by the SashContainer

The SashContainer modify the ContentProvider by the way of the ISashWindowsContentProvider interface. It doesn’t create models by itself.

The SashContainer transforms user interactions in calls to ISashWindowsContentProvider.  This should modify the ContentProvider internal structure. In turn, something must call the refresh() method on the SashContainer in order to refresh it (to force it to synchronize itself with the ContentProvider).

The SashContainer do not listen on the ContentProvider. This should be implemented externally.

2.3.        DI ContentProvider

The DiContentProvider is an implementation of the ISashWindowContentProvider.

It is implemented with an EMF metamodel, to save its content as EMF model.

The DiContentProvider is accessible throw the DiSashModelMngr class or TransactionalDiSashModelMngr class.

 

The internal EMF implementation is not intended to be used directly by users. Some interfaces are provided in order to access it: IPageMngr and  ISashWindowContentProvider.

 

IPageMngr, provides methods to add, open, remove Editors from the ContentProvider. This is the API used by user.

An implementation of the IPageMngr can be obtained from the DiSashModelMngr or the TransactionalDiSashModelMngr.

 

2.3.1.   DiSashModelMngr

To get an instance of the DiContentProvider, it is necessary to create an instance of DiSashModelMngr (or the Transactional one). This class requires two classes to work (Figure 4): an EMF Resource that is used to store/retrieve content, and a IPageModelFactory, used to create instance of IPageModel (model used by the SashContentProvider to create instance of nested Editor).

 

 

Figure 4 DiContentProvider Provided and Required Interfaces

 

 

2.3.2.   Internal Structure

The implementation is made of 3 parts:

·        The manager, used to retrieve the concrete implementations of provided interfaces (Figure 4)

·        Implementations of ISashWindowContentProvider and IPageMngr (Figure 4)

·        An EMF metamodel and model used to store/retrieve the sash structure (Figure 5).

 

Figure 5 The di sashwindows metamodel (internal structure).

 

The EMF metamodel is itself made of two parts:

·        A SashModel representing the structure of SashSystem. This is a tree structure whose leaf are objects representing pages (an Editor or a Component).

·        A PageList – a list of pages currently available for the SashSystem. This list contains objects representing all the pages, the open ones as well as the closed ones.

 

2.4.        AbstractMultiPageSashEditor

The Sash System provides an abstract base implementation of a multieditors. The class is AbstractMultiPageSashEditor. This implementation uses a SashContainer and requires a ISashWindowsContentProvider.

This later should be set by calling setContentProvider().

 

Figure 6 AbstractMultiPageSashEditor Overview

 

2.5.        SashContainerFactory

The SashContainerFactory class can be used to get an instance of the SashContainer, as this later is not directly accessible.

Note: this class is not yet implemented. It will be if there is a need for it.

2.6.        Glossary

Concept

Definition

SashContainer

A sash container is the main component of the sash system. It contains pages that can be arranged in multiple windows, and inside each window the pages can be arranged side by side or in sash folder. Two adjacent pages are separated by a sash allowing to resize the pages.

Sash Window

A sash window is a window that can be moved independently from the Eclipse window. A sash window show one or more pages of a Sash container.

Note: the actual implementation allows only one window docked inside Eclipse.

Sash Folder

Sash tabfolder

Folder

A sash folder renders pages in a tab fashion. A folder contains several pages, but show only one of them at a time. Pages can be selected with a tab.

Sash Panel

A sash panel render two

Sash Page

A sash page is the final

 

3.   Core / Backbone

The Core main intent is to provide a multieditors, that is, an editor of editors.

The provided multieditors is independent from the nested editors. These nested editors can be registered by using the Eclipse extension mechanism.

Nested editors can share objects thanks to the ServiceRegistry who register services. These services are registered programmatically or by using the Eclipse extension mechanism.

The multieditors use a Di ContentProvider to register the Sash content.

3.1.        Architecture Overview

The main class of the core is the CoreMultiDiagramEditor. It extends the sasheditor.AbstractMultiPageEditor to provide a multieditors. It adds some services needed by the nested editors.

Figure 7 shows the main classes and the added services.

 

Figure 7 Core multieditors overview.

All the services are also accessible thanks to the ServicesRegistry. This ServicesRegistry is provided to all nested editors while they are created. The nested editors can then retrieve any service it need.

3.2.        Public API

Main API of the core is represented in Figure 8.

From the main editor (aka instance of CoreMultiDiagramEditor) , it is possible to get the following members :

·        ServicesRegistry – To get other registered services

·        SashWindowsContainer – To get the active editor or refresh all editors

·        DiSashModelManager – To add, remove move, open, close diagrams in the editors

·        Main IEditorSite – To get the Eclipse SelectionService

·        The currently active nested editor. – Required by some GMF stuff

·        LifeCycleEventsProvider – To listen on life cycles events on the main editor (doSave, doSaveAs)

 

 

Figure 8 Core main APIs

 

·        ServiceRegistry – Used to get registered services

·         

3.3.        Available Services

The core creates and registers a number of services:

·        See org.eclipse.papyrus.core.editor.CoreMultiDiagramEditor.init(IEditorSite, IEditorInput)

·         

3.4.        ServiceRegistry

The ServiceRegistry is used to register services that can be found by a key, generally the classname of the service.

A service can be retrieve in a standard way, generally by using its ID or its implementation class.

 

3.4.1.   Architecture Overview

Figure 9 shows the architecture of the ServiceRegistry.

 

Figure 9 ServiceRegistry Architecture

3.4.2.   Designing a Service

A service is a class providing some methods.

The class can be a Pojo, or it can extend the IService interface.

If the service extends the IService interface, the ServiceRegistry will call the life cycle methods on the service.

3.4.3.   Register a Service

A service can be registered in several ways:

 

Using the Eclipse extension mechanism

A service can be registered using the Eclipse extension mechanism.

Figure 10 shows the extension names and fields.

 

Figure 10 Register a Service with Eclipse extension

 

Programmatically

Services can be registered programmatically using one of the add(…) methods (Figure 11). In this case, the service can be a Pojo.

Figure 11 ServicesRegistry class public API.

3.5.        EditorRegistry

The EditorRegistry is used to register the descriptors of editors that can be created in the multieditor.

Editor descriptors can be registered using the Eclipse extension mechanism (Figure 12).

 

Figure 12 Nested Editor registered with Eclipse Extension

The EditorRegistry is mainly used by the SashContainer to create nested Editors when requested by the user, or when the multieditor is reopened.

The EditorRegistry requires the following to work:

·         

4.   Nested Editors

A nested editor, or embedded editor, is an editor that can be created and rendered in the multieditors.

A nested editor can be a regular Eclipse editor implementing the IEditorPart interface.

There is several ways to create nested editor, depending on which package you use (Sash only, Core, …).

Usually, all nested editors share common resources, like the EMF Resource. This can be achieved with the help of the ServiceRegistry.

4.1.        Sash Requirements for nested Editors

 

On the Sash level, it is necessary to provide a ContentProvider managing the structure of rendered editors.

The sash package provides 3 implementations for the ContentProvider:

·        org.eclipse.papyrus.sasheditor.contentprovider.singlefolder

o      Mainly used for test purpose. This implementation only allows one tabfolder, and all nested editor are in this tabfolder. Implementation using this ContentProvider will react as the original Eclipse MultiTabEditor.

·        org.eclipse.papyrus.sasheditor.contentprovider.simple

o      A simple implementation where the structure is only maintained in memory. There is no way to save the structure. This is an example design.

·        org.eclipse.papyrus.sasheditor.contentprovider.di

o      An implementation done with an EMF model called sashdi. This implementation allows save and restore of nested EMF based editors.

4.2.        Sash Di Requirements for nested Editors

4.3.        Core Requirements for nested Editors

The Core provides some services helping to manage nested editors.

To enable a nested editor in the multieditor, it is necessary to:

·        Register the nested editor in the Core Editor Factory

·        Enable an action creating an new instance of the editor

 

You need to provide the following;

·        An Editor (not covered here)

·        An Action or an Handler allowing to create the EObject representing the editor and add this EObject to the SashContentProvider

·        A factory that will receive as input the previously created EObject, and will create an IEditorModel for your editor.

·        A subclass of IEditorModel. This class is used as model by the SashEditor. It serves to create the instance of the Editor for the previously created EObject.

4.3.1.   Environment Provided by Core

The Core provide following services that can be helpful when enabling a nested editor:

·        PageModelFactoryRegistry – Used to register a nested editor. Editors are registered with the help of the Eclipse extension mechanism.

·        ActionBarContributorRegistry – Used to share common ActionBarContributor between nested editor

·        ServicesRegistry – Used to share services across nested editors.

·        IPageMngr – Used to add or open an editor instance

·        DiResourceSet – Use to get the available models.

4.3.2.   Registering Nested Editor

You need an editor allowing to save its representation as an EMF model. GMF editors falls in this category, as well as EMF ones.

Once you have such an editor, you need to:

·        Provide an implementation of IPluggableEditorFactory

·        Provide an implementation of IEditorModel

·        Register your editor with the Eclipse extension mechanism

 

The core impose some requirements on its nested editors: mainly, such editor should rely on an EMF model.

Editor Requirements

·        Your Editor should answer to following requirements:

o      Implements org.eclipse.ui.IEditorPart

o      Can be represented by an EObject. That is, it is possible to 1) Get an EObject representing the type and the content of the editor. 2) Reopen the editor from the EObject. GMF editors fall in this category.

IPluggableEditorFactory

You need to provide an implementation of this class for your editor. This class will be used to register your editor in the Core.

The sash editor will ask the PageModelFactoryRegistry for an editor factory suitable for a specified EObject.

When found, the sash editor ask for the IEditorModel for this EObject.

IEditorModel

Implementations of this interface are used to create instances of editors.

The class provides methods to:

·        Create the editor instance – Here you should instanciate your editor.

·        Get the ActionBarContributor associated to the editor

·        Get the editor icon and name

·        …

Register as Eclipse Extension

The core load all EditorFactories registered as Eclipse extension (Figure 13).

 

Figure 13 Registering an nested Editor

 

4.3.1.   Create Editor Instance Action

 

To add a nested editor to the core, following actions should be done:

  1. Create the nested editor EMF representation

·        Method org.eclipse.papyrus.core.adaptor.gmf.AbstractPapyrusGmfCreateDiagramCommandHandler.runAsTransaction(DiResourceSet, EObject, String) shows  an (complex) example of how a GMF diagram can be created.

  1. Add the root EObject representing the nested editor to the DiContentProvider by using the IPageMngr.openPage(eObject) method.

·        The IPageMngr can be obtained by calling org.eclipse.papyrus.core.utils.EditorUtils.getIPageMngr()

  1. The SashSystem should react automatically and show the newly added nested editor (if it know how to handle such editor ).

 

To let the Core handle a nested editor, following things are required:

 

There can have several Action classes used to create a new editor. All the implementations should do what is described before (create a notation.Diagram and add it to the DiContentProvider). Example of implementation can be found for the class diagram:

 

I would like to provide a more general mechanism for the creation wizard. This mechanism will complete the Eclipse mechanism for declaring menus and toolbar actions. It will allow:

 

From the core point of view, an nested editor can be added to the multieditor by adding the EObject representing the nested editor to the IPageMngr. Figure 13 shows what happen when an nested editor is added to the DiContentProvider.

 

Figure 14 Adding nested Editor - Core Point of View

4.3.1.   Example

A complete example can be found here:

org.eclipse.papyrus.diagram.umltools.clazz