Sash and
Core
Architectures
Author : Cedric Dumoulin
Date : 6 dιc. 2009
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
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.
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.
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.
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:
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.
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.
The
SashContainer modify the ContentProvider by the way of the
ISashWindowsContentProvider interface. It doesnt 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.
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.
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
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.
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
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.
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 |
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.
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.
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
·
The core creates and registers a number of services:
·
See org.eclipse.papyrus.core.editor.CoreMultiDiagramEditor.init(IEditorSite,
IEditorInput)
·
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.
Figure 9 shows the architecture of the ServiceRegistry.
Figure 9 ServiceRegistry Architecture
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.
A service
can be registered in several ways:
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
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.
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:
·
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.
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.
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.
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.
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.
·
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.
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.
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
·
The core load all EditorFactories registered as Eclipse extension (Figure 13).
Figure 13 Registering an nested Editor
To add a
nested editor to the core, following actions should be done:
·
Method
org.eclipse.papyrus.core.adaptor.gmf.AbstractPapyrusGmfCreateDiagramCommandHandler.runAsTransaction(DiResourceSet,
EObject, String) shows an (complex) example of how a GMF diagram can
be created.
·
The
IPageMngr can be
obtained by calling org.eclipse.papyrus.core.utils.EditorUtils.getIPageMngr()
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
A complete example can be found here:
org.eclipse.papyrus.diagram.umltools.clazz