Papyrus Banner
  1. Introduction
  2. Papyrus Decoration Service Framework
    1. Architecture
      1. DecorationService
        1. Main methods
      2. IPapyrusMarker
        1. Main methods
      3. IDecorationSpecificFunctions
        1. Main methods
      4. Decoration Image Extension Point
  3. Shape Service Framework
    1. Architecture
      1. AbstractShapeProvider
        1. Main methods
      2. ShapeProvider Extension Point
      3. Appearence tab
  4. GMF Decorator Service Framework
    1. Architecture
      1. IDecoratorProvider
        1. Main methods
      2. AbstractDecorator
        1. Main methods
      3. IDecoratorTarget
        1. Main methods
      4. GMF Decorator Provider Extension Point
  5. Application Example
    1. Context
      1. Example
      2. Using Decoration Service
        1. Implemented architecture
        2. Implemented markers
        3. Implemented NodeDecoration EditPolicy
        4. Implemented CustomEditPolicy Provider
        5. Implemented Decoration Specific Function
        6. Implementation without markers
          1. Architecture
          2. Implemented Edit Policy
        7. Result
      3. Using Shape Service
        1. Implemented architecture
        2. Implemented Shape Provider
        3. Implemented Shape Provider Extension Point
        4. Result
      4. Using GMF Decorator Service
        1. Implemented architecture
        2. Implemented Decorator Provider
        3. Implemented AbstractDecorator
        4. Implemented GMF Decorator Provider Extension Point
        5. Result
    2. Sources

Introduction

The purpose of this documentation is to give the minimum information to deploy your own decorator for Papyrus elements as EditPart. To manage decorator on Edit Part two solutions are provided by Papyrus:

Decoration Service is compliant with gif, ico, bmp, jpg and png files. Contextual message can be set for each decoration, and propagation to parents can be allowed. Unlike Decoration Service, Shape Service is only compliant to svg files, but the position of the shape can be selected within in the properties view. Others differences are that the shape can be displayed in the symbol compartment and it can be used as the shape of the figure.

GMF Decorator Service Framework can also be used to display image. As the Decoration Service from papyrus you will be available to display several formats of images.

Papyrus Decoration Service Framework

Architecture

The decoration service architecture has already been described on a document ( here). You will find a description of main interfaces or classes to be implemented or to be called in order to use the framework and add specific decoration.

DecorationService

Decoration service permits to manage decoration on EObject. Decorations are added through this service. It provides listener to observe changes. This service can be retrieved with:

DecorationService decorationService = ServiceUtilsForEditPart.getInstance().getService(DecorationService.class, editPart);

Main methods

IPapyrusMarker

IPapyrusMarker provides a protocol for markers that annotates elements with information, often used for problems. It is an analogy of the Eclipse IMarker API for resources in the workspace, to be implemented in order to create its own marker used for custom decoration.

Main methods

IDecorationSpecificFunctions

This interface allows to access a set of functions that depends on the decorator type. The objective is that plug-ins for a specific decoration type can implement this interface (via an extension point) to provide the information that depends on the decoration type, notably the used icons, their position, the way how messages are calculated and how decoration might propagate from children to parents. To be implemented in order to create its own decoration.It Is related to a marker through decorationImage extension point.

Main methods

Decoration Image Extension Point

Provide an ImageDescription for a decoration marker. It allows to link a IDecorationSpecificFunction class to a Papyrus marker through his type.

Identifier: org.eclipse.papyrus.infra.services.decoration.decorationImage

Shape Service Framework

The shape service framework allows to display svg document in the symbol compartment or at a chosen position on the edit part.

Architecture

AbstractShapeProvider

The framework provides a simple abstract provider class in order to add svg shape to edit parts. Only few methods have to be implemented.

Main methods

ShapeProvider Extension Point

To be loaded, implemented shape providers have to be linked through a specific extension point.

Appearence tab

To display the shape on the edit parts and to choose the visibility and the position, attributes have to be set on the appearance tab of the properties view.

GMF Decorator Service Framework

GMF provide a decorator service to display decorations on views. An extension point exists to use to load your custom decorator provider. By default the position of decoration is set according to the direction, but you can customize its location throught a locator(org.eclipse.draw2d.Locator).

Architecture

IDecoratorProvider

Clients providing an extension to the DecoratorService need to create a decorator provider class that implements the IDecoratorProvider interface. IDecoratorProvider is the interface for providers of the decorator service. A decorator provider is responsible for installing its decorators on the decorator targets that it wishes to decorate.

Main methods

AbstractDecorator

Clients can have their decorator subclass this to inherit utility methods for adding decoration figures.

Main methods

IDecoratorTarget

It's the interface of targeted element. It will not implemented by client. Decorator in decorator target have some properties which can be set:

Main methods

GMF Decorator Provider Extension Point

his extension point is used to define decorator providers for the decorator service (org.eclipse.gmf.runtime.diagram.ui.services.decorator). The Decoration Service gives clients the ability to decorate diagram elements with an image or figure.

Application Example

  1. Introduction
  2. Papyrus Decoration Service Framework
    1. Architecture
      1. DecorationService
        1. Main methods
      2. IPapyrusMarker
        1. Main methods
      3. IDecorationSpecificFunctions
        1. Main methods
      4. Decoration Image Extension Point
  3. Shape Service Framework
    1. Architecture
      1. AbstractShapeProvider
        1. Main methods
      2. ShapeProvider Extension Point
      3. Appearence tab
  4. GMF Decorator Service Framework
    1. Architecture
      1. IDecoratorProvider
        1. Main methods
      2. AbstractDecorator
        1. Main methods
      3. IDecoratorTarget
        1. Main methods
      4. GMF Decorator Provider Extension Point
  5. Application Example
    1. Context
      1. Example
      2. Using Decoration Service
        1. Implemented architecture
        2. Implemented markers
        3. Implemented NodeDecoration EditPolicy
        4. Implemented CustomEditPolicy Provider
        5. Implemented Decoration Specific Function
        6. Implementation without markers
          1. Architecture
          2. Implemented Edit Policy
        7. Result
      3. Using Shape Service
        1. Implemented architecture
        2. Implemented Shape Provider
        3. Implemented Shape Provider Extension Point
        4. Result
      4. Using GMF Decorator Service
        1. Implemented architecture
        2. Implemented Decorator Provider
        3. Implemented AbstractDecorator
        4. Implemented GMF Decorator Provider Extension Point
        5. Result
    2. Sources

This part will provide a simple example to use available services to display decorations on edit part according to the conditions described above.

Context

SysML profile will be used as allocation profile. An "allocation table" affects the call behavior to nodes. Nodes can be stereotyped with NodeA and NodeB stereotypes. Depending on the node on which the callBehavior is allocated, the decorator of the callBehavior should change.

Example

With this profile:

Applied on these two nodes:

And with this allocation Table:

The expected result is:

Using Decoration Service

Implemented architecture

On this example, we have used 2 markers and one decoration specific function for each marker. The association between markers and decoration are done thanks to a specific extension point. An Edit policy is used to add markers to the decoration Service according to the model context. The edit policy is installed to the right edit parts through an edit policy provider, which is loaded by extension point.

Implemented markers

For each type of stereotype NodeA and NodeB, a marker have been implemented with a specific markerType:

public class NodeAMarker implements IPapyrusMarker {
	public static final String MARKER_TYPE = "org.eclipse.papyrus.infra.services.decoration.example.NodeA"; //$NON-NLS-1$
	public static final String NODE_A_STEREOTYPE = "DecorationExampleProfile::NodeA";//$NON-NLS-1$
	protected View notationElement;
	public NodeAMarker(final View notationElement) {
		this.notationElement = notationElement;
	}
	@Override
	public boolean exists() {
		return ExampleUtils.isAllocatedTo((CallBehaviorAction) notationElement, NODE_A_STEREOTYPE);
	}
	@Override
	public String getType() throws CoreException {
		return MARKER_TYPE;
	}
	@Override
	public String getTypeLabel() throws CoreException {
		return "Node A marker example";
	}
	...

Implemented NodeDecoration EditPolicy

These markers are added to the DecorationService thanks to an editPolicy applied to the CallBehavoir edit part.

The DecorationService is called in the Activate() method:

	@Override
	public void activate() {
		super.activate();
		// TODO install listener
		try {
			decorationService = ServiceUtilsForEditPart.getInstance().getService(DecorationService.class, getHost());
			refresh();
		} catch (final ServiceException ex) {
			// Ignored; do nothing
		}
	}

The refresh method adds or removes markers according to the allocated element on the UML element of the edit part:

	@Override
	public void refresh() {
		...
		// If the marker already set for nodeA have to be changed.
		if (ExampleUtils.isAllocatedTo((CallBehaviorAction) view.getElement(), NodeAMarker.NODE_A_STEREOTYPE) != isNodeAMarked) {
			isNodeAMarked = !isNodeAMarked;
			if (isNodeAMarked) {
				decorationService.addDecoration(getMarkerA(), getView());
			} else {
				decorationService.removeDecoration(getMarkerA().toString());
			}
			getHost().refresh();
		}
		// If the marker for nodeB already set have to be changed
		if (ExampleUtils.isAllocatedTo((CallBehaviorAction) view.getElement(), NodeBMarker.NODE_B_STEREOTYPE) != isNodeBMarked) {
			isNodeBMarked = !isNodeBMarked;
			if (isNodeBMarked) {
				decorationService.addDecoration(getMarkerB(), getView());
			} else {
				decorationService.removeDecoration(getMarkerB().toString());
			}
			getHost().refresh();
		}
	}

Implemented CustomEditPolicy Provider

An edit policy provider is used to install the NodeDecorationEditPolicy to the right edit parts.

public class CustomEditPolicyProvider implements IEditPolicyProvider {
	protected String diagramType = org.eclipse.papyrus.uml.diagram.activity.edit.parts.ActivityDiagramEditPart.MODEL_ID;
	@Override
	public void addProviderChangeListener(final IProviderChangeListener listener) {
	}
	@Override
	public boolean provides(final IOperation operation) {
		boolean provide = false;
		String currentDiagramType;
		// get the element
		final EObject referenceElement = ((View) ((CreateEditPoliciesOperation) operation).getEditPart().getModel()).getElement();
		// Test if it's a creation operation and the element is a CallBehavoirActino
		if ((operation instanceof CreateEditPoliciesOperation) && (referenceElement instanceof CallBehaviorAction)) {
			// Get The current diagram type
			currentDiagramType = ((View) ((CreateEditPoliciesOperation) operation).getEditPart().getModel()).getDiagram().getType();
			// Test if we are on an Activity Diagram
			if ((diagramType != null) && (diagramType.equals(currentDiagramType))) {
				provide = true;
			} else {
				provide = false;
			}
		}
		return provide;
	}
	@Override
	public void removeProviderChangeListener(final IProviderChangeListener listener) {
	}
	@Override
	public void createEditPolicies(final EditPart editPart) {
		editPart.installEditPolicy(NodeDecoratorEditPolicy.EDIT_POLICY_ROLE, new NodeDecoratorEditPolicy());
	}
}

This edit policy provider is installed with the editpolicyProviders extension point:

<extension
       point="org.eclipse.gmf.runtime.diagram.ui.editpolicyProviders">
    <editpolicyProvider
          class="org.eclipse.papyrus.infra.services.decoration.example.provider.CustomEditPolicyProvider">
       <Priority
             name="Medium">
       </Priority>
    </editpolicyProvider>
 </extension>

Implemented Decoration Specific Function

The decoration specific function specifies the decoration to be applied on a marked element. It defines the image, the position, the context message of the decoration etc...

public class NodeADecoration implements IDecorationSpecificFunctions {
	@Override
	public MarkChildren supportsMarkerPropagation() {
		// This marker should not be propagated
		return null;
	}
	@Override
	public ImageDescriptor getImageDescriptorForGE(final IPapyrusMarker marker) {
		return org.eclipse.papyrus.infra.widgets.Activator.getDefault().getImageDescriptor(Activator.ID, "icons/NodeA.gif"); //$NON-NLS-1$
	}
	@Override
	public PreferedPosition getPreferedPosition(final IPapyrusMarker marker) {
		return PreferedPosition.NORTH_EAST;
	}
	@Override
	public String getMessage(final IPapyrusMarker marker) {
		return "Node A decoration example";
	}
	@Override
	public int getPriority(final IPapyrusMarker marker) {
		return 0;
	}
}


To associate decoration specific function to a marker, an extension point can be used. In the image below the decoration NodeAdecoration is linked to the marker type defined in the marker NodeAMarker.MARKER_TYPE

   <extension
 		point="org.eclipse.papyrus.infra.services.decoration.decorationSpecificFunctions">
	  <client
		class="org.eclipse.papyrus.infra.services.decoration.example.NodeADecoration"
		decorationType="org.eclipse.papyrus.infra.services.decoration.example.NodeA">
	  </client>
	  <client
		class="org.eclipse.papyrus.infra.services.decoration.example.NodeBDecoration"
		decorationType="org.eclipse.papyrus.infra.services.decoration.example.NodeB">
	  </client>
   </extension>


Implementation without markers

NOTE:this implementation does't work for now due to a NPE. A Patch is in progress to fix that.

Architecture

Without marker the architecture of class to implement is less complexe. You only needs the edit policy which will add decoration to the decorationService with the provider which is the same than with marker.

Implemented Edit Policy

The edit policy is prtty much the same except that we use addDecoration(String id, String type, EObject element, ImageDescriptor decorationImageForGE, ImageDescriptor decorationImageForME, PreferedPosition position, String message, int priority)

So in the refresh methode for NodeB:

...
// If the marker for nodeB already set have to be changed
if (ExampleUtils.isAllocatedTo((CallBehaviorAction) view.getElement(), ExampleUtils.NODE_B_STEREOTYPE) != nodeBMarked) {
	nodeBMarked = !nodeBMarked;
	if (nodeBMarked) {
		// Decoration Without marker
		decorationService.addDecoration(
				view.getElement().toString(), // $NON-NLS-1$ 
				"NodeBDecoration", //$NON-NLS-1$ 
				view.getElement(),
				org.eclipse.papyrus.infra.widgets.Activator.getDefault().getImageDescriptor(Activator.ID, "icons/NodeB.gif"), //$NON-NLS-1$
				org.eclipse.papyrus.infra.widgets.Activator.getDefault().getImageDescriptor(Activator.ID, "icons/NodeB.gif"), //$NON-NLS-1$ ,
				PreferedPosition.SOUTH_WEST,
				"NodeB decoration Without marker",
				0);
	} else {
		decorationService.removeDecoration(view.getElement().toString());
	}
	getHost().refresh();
}
...


Result

Using Shape Service

Implemented architecture

The use of Shape service is done by the implementation of a shape provider which extends AbstractShapeProvider: " NodeShapeProvider". Then " NodeShapeProvider" is activated thanks to the shape provider extension point.

Implemented Shape Provider

The implementated Shape provder will provide shape according to the same conditions than before.

public class NodeShapeProvider extends AbstractShapeProvider {
	// The used SVG files
	private static final String ICONS_SYMBOLS_NODE_A_SVG = "/icons/NodeA.svg";//$NON-NLS-1$
	private static final String ICONS_SYMBOLS_NODE_B_SVG = "/icons/NodeB.svg";//$NON-NLS-1$
	...
	@Override
	public boolean providesShapes(final EObject view) {
		boolean provide = false;
		if (view instanceof View) {
			final EObject element = ((View) view).getElement();
			// Test if the element is a CallBehavoirAction and if a stereotyped node is allocated to it.
			if (element instanceof CallBehaviorAction
					&& (ExampleUtils.isAllocatedTo((CallBehaviorAction) element, ExampleUtils.NODE_A_STEREOTYPE)
							|| ExampleUtils.isAllocatedTo((CallBehaviorAction) element, ExampleUtils.NODE_B_STEREOTYPE))) {
				provide = true;
			} else {
				provide = false;
			}
		}
		return provide;
	}
	@Override
	public List<RenderedImage> getShapes(final EObject view) {
		if (providesShapes(view)) {
			// Gets the SVG document: here NodeA.svg or/and NodeB.svg
			final List<SVGDocument> documents = getSVGDocument(view);
			if (documents != null) {
				final List<RenderedImage> result = new LinkedList<RenderedImage>();
				for (final SVGDocument document : documents) {
					try {
						// Adds the shape to result
						result.add(renderSVGDocument(view, document));
					} catch (final IOException ex) {
						// Do nothing
					}
				}
				return result;
			}
		}
		return null;
	}
	@Override
	public List<SVGDocument> getSVGDocument(final EObject view) {
		final List<SVGDocument> svgDocuments = new ArrayList<SVGDocument>();
		if (providesShapes(view)) {
			final CallBehaviorAction callBehaviorAction = (CallBehaviorAction) ((View) view).getElement();
			// If allocation corresponding adds SVG document for Node A
			if (ExampleUtils.isAllocatedTo(callBehaviorAction, ExampleUtils.NODE_A_STEREOTYPE)) {
				// Get the SVG document
				final SVGDocument document = getSVGDocument(URI.createPlatformPluginURI(Activator.ID + ICONS_SYMBOLS_NODE_A_SVG, true).toString());
				if (document != null) {
					svgDocuments.add(document);
				}
			}
			// If allocation corresponding adds SVG document for Node B
			if (ExampleUtils.isAllocatedTo(callBehaviorAction, ExampleUtils.NODE_B_STEREOTYPE)) {
				// Get the SVG document
				final SVGDocument document = getSVGDocument(URI.createPlatformPluginURI(Activator.ID + ICONS_SYMBOLS_NODE_B_SVG, true).toString());
				if (document != null) {
					svgDocuments.add(document);
				}
			}
		}
		return svgDocuments;
	}
	...
}


Implemented Shape Provider Extension Point

To activate the shape provider we use the Shape Provider Extension Point:

<extension point="org.eclipse.papyrus.infra.gmfdiag.common.shapeProvider">
   <shapeProvider 
      class="org.eclipse.papyrus.infra.services.decoration.example.provider.NodeShapeProvider"
      id="org.eclipse.papyrus.infra.services.decoration.example.nodeShapeProvider"
      name="NodeShapeProvider">
      <Priority 
         name="Medium">
      </Priority>
   </shapeProvider>
</extension>

Result

Using GMF Decorator Service

GMF Decorator framework can be implemented by many ways. The choice made here is to use Figure as decorator which contains icons. This will permits to position icons on the figure. The to locate the figure in the target à specific locator will be implemented. This permits to have a better control of the location of decoration. A tootip will be also added.

Implemented architecture

To implement it we create a provider loaded through the specific extension point. The provider will install the node decorator which use the specific locator.

Implemented Decorator Provider

/**
 * Example of provider for display decoration using gmf decorator framework.
 */
public class GMFDecoratorProvider extends AbstractProvider implements IDecoratorProvider {
	// The key of the installed decorator
	private static final String GMF_DECORATION_KEY = "gmf_decoration_example_key"; //$NON-NLS-1$
	@Override
	public boolean provides(final IOperation operation) {
		boolean provide = false;
		// it needs to be a CreateDecoratorsOperation
		if (operation instanceof CreateDecoratorsOperation) {
			final IDecoratorTarget decoratorTarget = ((CreateDecoratorsOperation) operation).getDecoratorTarget();
			final View view = decoratorTarget.getAdapter(View.class);

			// Test if the decoratorTarget element is a CallBehavoirAction
			if (view != null && ActivityDiagramEditPart.MODEL_ID.equals(UMLVisualIDRegistry.getModelID(view)) && view.getElement() instanceof CallBehaviorAction) {
				provide = true;
			} else {
				provide = false;
			}
		}
		return provide;
	}
	@Override
	public void createDecorators(final IDecoratorTarget decoratorTarget) {
		final View node = decoratorTarget.getAdapter(View.class);
		if (node != null) {
			// Install the decorator
			decoratorTarget.installDecorator(GMF_DECORATION_KEY, new NodeDecorator(decoratorTarget));
		}
	}
}

Implemented AbstractDecorator

The refresh methods will permit to adds the decoration.

...
@Override
public void refresh() {
	//First of all, we remove decoration
	removeDecoration();
	final EditPart editPart = (EditPart) getDecoratorTarget().getAdapter(EditPart.class);
	if (editPart instanceof NodeEditPart) {
		// Get the figure of the editpart
		final IFigure editPartFigure = ((GraphicalEditPart) editPart).getFigure();
		// Get the view of the editpart
		final View view = (View) editPart.getModel();

		// Create a main figure with a flow layout. It will contains icons.
		final IFigure figure = new Figure();
		final FlowLayout flowLayout = new FlowLayout();
		flowLayout.setHorizontal(true);
		flowLayout.setMinorSpacing(0);
		figure.setLayoutManager(flowLayout);


		// If for nodeA is allocated to the call behavior
		if (ExampleUtils.isAllocatedTo((CallBehaviorAction) view.getElement(), ExampleUtils.NODE_A_STEREOTYPE)) {
			// Create child figure
			final ScaledImageFigure imageFigureA = getImageNode("icons/NodeA.gif");//$NON-NLS-1$
			// Add it to main figure of the decoration
			figure.add(imageFigureA);
		}
		// If for nodeA is allocated to the call behavior
		if (ExampleUtils.isAllocatedTo((CallBehaviorAction) view.getElement(), ExampleUtils.NODE_B_STEREOTYPE)) {
			// Create child figure
			final ScaledImageFigure imageFigureB = getImageNode("icons/NodeB.gif");//$NON-NLS-1$
			// Add it to main figure of the decoration
			figure.add(imageFigureB);
		}

		// Get MapMode
		final IMapMode mm = MapModeUtil.getMapMode(editPartFigure);
		// Set the size of the decorator figure
		figure.setSize(mm.DPtoLP(IMAGE_WIDTH * figure.getChildren().size()) + 2, mm.DPtoLP(IMAGE_HEIGHT));

		// Get the direction/position of the decoration. Direction will be takes from shape direction in appearance tab of properties view.
		final Direction direction = getDirection(view);

		// Instantiate a custom locator to have decoration inside the figure.
		final Locator locator = new InsideDirectedLocator(editPartFigure, direction);
		// Set the decoration with the custom locator.
		setDecoration(getDecoratorTarget().addDecoration(figure, locator, false));

		//Set the tool tip of the decoration
		getDecoration().setToolTip(new Label("GMF Decorations"));
	}
}

the Figure is get with the following method:

/**
 * Create a ScaledImageFigure for a specific relative location of a image.
 * 
 * @param imageLocation
 * @return the image.
 */
private ScaledImageFigure getImageNode(final String imageLocation) {
	final ImageDescriptor imageDescriptor = org.eclipse.papyrus.infra.widgets.Activator.getDefault().getImageDescriptor(Activator.ID, imageLocation);
	final Image image = imageDescriptor.createImage();
	final ScaledImageFigure imageFigure = new ScaledImageFigure();
	imageFigure.setImage(image);
	imageFigure.setSize(IMAGE_WIDTH, IMAGE_HEIGHT);
	return imageFigure;
}

If you don't want to use a custom locator use instead of addDecoration. A negative margin will put the decoration inside and a positive will put it outside.

	setDecoration(getDecoratorTarget().addShapeDecoration(figure, direction, -1, false));

Implemented GMF Decorator Provider Extension Point

To activate the GMF Decorator Provider we use the Extension Point org.eclipse.gmf.runtime.diagram.ui.decoratorProviders :

 <extension
       point="org.eclipse.gmf.runtime.diagram.ui.decoratorProviders">
    <decoratorProvider
          class="org.eclipse.papyrus.example.decoration.provider.GMFDecoratorProvider">
       <Priority
             name="Medium">
       </Priority>
    </decoratorProvider>
 </extension>

Result

Sources

The plugin of this example can be retrieved in the Papyrus git code repository here or imported in File>New>Others...>Examples>Papyrus Example>Decoration Services Examples.