GMF generated editors generate a parser providers extension with the priority set to
Lowest. Depending on the sequence in which plug-ins are loaded, the UML parser provider
may interfere when utilizing the generated editor. To ensure that the correct parser provider is
chosen when using a generated editor, modify the parser provider extension, setting the priority
to any priority above Lowest (choose Low, Medium, High, or Highest).
This can be done by either setting the parser priority value in the gmfgen
model parser and regenerating
the editor, or by simply editing the plugin.xml file manually.
<extension point="org.eclipse.gmf.runtime.common.ui.services.parserProviders"> <ParserProvider class="org.eclipse.gmf.ecore.providers.EcoreParserProvider"> <Priority name="Low"/> </ParserProvider> </extension>
When generating GMF editors using GMF tooling, it is important to ensure that the model ID
of the generated editor is unique. When a gmfgen
model is created from the gmfmap
model,
the model ID gets assigned a default value. The default value comes from the model name
attribute in the genmodel
. To ensure that generated editors do no interfere with other
generated editors, it is required that the model ID be changed to a unique string.
For example, GMF tooling provides an editor for Ecore models. The model ID of the generated editor is "Ecore". If a new editor were to be generated for the Ecore metamodel, the same model ID would be used as the default value. This could result in provider code which incorrectly provides for the wrong editor.
To change the model ID, open up the gmfgen
model file, and navigate to the Gen Editor Generator
entry. Open up the properties view to modify the model ID. Then regenerated the editor.
The following describes how to modify a GMF generated editor to allow the generated shapes to be created onto a Freeform diagram. To accomplish this, the generated tooling must be contributed to the Freeform diagram palette. The code snippets used in this example are taken from the GMF Ecore diagram editor.
The first step is to contribute the generated palette tools to the Freeform diagram
by extending the
org.eclipse.gmf.runtime.diagram.ui.paletteProviders
extension-point.
paletteProvider
with class
set to the palette provide class
which will be created right after.Priority
.content
.method
to the content
and set the name of the method to be
getDiagram().getType()
and the value to be Freeform
. This means that this
palette provider will contribute to the editor when the diagram type is of type Freeform
.<extension point="org.eclipse.gmf.runtime.diagram.ui.paletteProviders"> <paletteProvider class="org.eclipse.gmf.ecore.providers.EcorePaletteProvider"> <Priority name="Lowest"/> <content> <method name="getDiagram().getType()" value="Freeform"/> </content> </paletteProvider> </extension>
The next step is to create the palette provider class as shown below. This provider will delegate contributing to
the palette to the generated palette factory named XXXPaletteFactory
.
public class EcorePaletteProvider extends AbstractProvider implements IPaletteProvider { public void contributeToPalette(IEditorPart editor, Object content, PaletteRoot root, Map predefinedEntries) { new EcorePaletteFactory().fillPalette(root); } public void setContributions(IConfigurationElement configElement) { // do nothing } public boolean provides(IOperation operation) { return true; } }
The generated palette tools are contributed within a
PaletteGroup
on the palette, rather than a
PaletteDrawer
. Since the RMP UML Modeler has many palette tools, it is suggested that the
generated tools be added to a drawer to reduce the clutter.
To do this, open up the gmftool
model file, and navigate to each Tool Group
entry. Open up the properties view to modify the Collapsible
property to True
.
At the same time, change the Title
property to be more meaningful by prepending the existing
value with the model name. Then regenerated the editor.
By integrating a generated editor into the RMP UML Modeler, the model ID restriction needs to be removed from the generated editor. The model ID restriction restricted generated views and edit parts to only be allowed to be created within the generated editor. This restriction needs to be more lenient to allow for the creation of these items on the Freeform diagram.
The generated visual IDs are unique only when used within the generated editor. When integrating multiple generated editors into the RMP UML Modeler, the visual IDs must be changed such that they are unique IDs across all integrated editors. Modify the visual IDs in the genmodel to be unique (possibly prefix the same unique value to each visual ID) and regenerate the editor.
In the generated XXXVisualIDRegistry
class, add an if-statement to the beginning
of the getModelID(View view)
method which checks if the view is part of the
generated editor, and if so returns the generated editors' model ID.
public static String getModelID(View view) { if (isOfEcoreModel(view)) { return EPackageEditPart.MODEL_ID; } ... }
The implementation method used above to check if the view is part of the generated editor simply switches on the visual ID, which comes from the view type, and compares it to all generated visual IDs. Obtain the visual IDs to construct the case statements from the generated edit parts.
These utility methods should be added to XXXVisualIDRegistry
.
public static boolean isOfEcoreModel(View view) { return isOfEcoreModel(getVisualID(view.getType())); } public static boolean isOfEcoreModel(int visualID) { switch (visualID) { case EClassEditPart.VISUAL_ID: case EClassNameEditPart.VISUAL_ID: ... return true; } return false; }
Finally, a restriction to only allow creation of generated elements within the generated editor must be removed
from the visual ID registry. Locate getNodeVisualID
and canCreateNode
methods
within the XXXVisualIDRegistry
and update them according to the code snippets below.
public static int getNodeVisualID(View containerView, EObject domainElement) { // replace the entire top portion of the method with the following if (domainElement == null) { return -1; } String containerModelID = org.eclipse.gmf.ecore.part.EcoreVisualIDRegistry.getModelID(containerView); int containerVisualID; if (EPackageEditPart.MODEL_ID.equals(containerModelID)) { containerVisualID = org.eclipse.gmf.ecore.part.EcoreVisualIDRegistry.getVisualID(containerView); } else { if (containerView instanceof Diagram) { containerVisualID = EPackageEditPart.VISUAL_ID; } else { return -1; } } switch (containerVisualID) { ... // use the existing case statements ... } return -1; }
public static boolean canCreateNode(View containerView, int nodeVisualID) { ... // comment out this restriction // if (!EPackageEditPart.MODEL_ID.equals(containerModelID) && !"ecore".equals(containerModelID)) { //$NON-NLS-1$ // return false; // } ... }
GMF generated editors replace the default edit policy for the
EditPolicyRoles.SEMANTIC_ROLE
with a generated semantic edit policy for each edit part.
These generated semantic edit policies are responsible for creating commands which manipulate the semantic model,
be it element creation, element destruction, attribute value setting etc... In order to obtain
these semantic commands when creating elements on the Freeform diagram, the generated diagram
semantic edit policy needs to be installed on the Freeform diagram edit part.
This is done by extending the
org.eclipse.gmf.runtime.diagram.ui.editpolicyProviders
extension-point.
editpolicyprovider
and set the class
to be a class which implements
org.eclipse.gmf.runtime.diagram.ui.services.editpolicy.IEditPolicyProvider
.org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart
whose notation view type is Freeform.<extension point="org.eclipse.gmf.runtime.diagram.ui.editpolicyProviders"> <editpolicyProvider class="org.eclipse.gmf.ecore.providers.EcoreEditPolicyProvider"> <Priority name="Medium"/> <object class="org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart(org.eclipse.gmf.runtime.diagram.ui.editparts)" id="FreeformDiagram"> <method name="getNotationView().getType()" value="Freeform"/> </object> <context editparts="FreeformDiagram"/> </editpolicyProvider> </extension>
The edit policy provider class should include
the same, or stricter, provides criteria defined in the xml extension. It should also implement
createEditPolicies(EditPart ep)
to create and install the edit policy on the
given edit part. An extension of XXXItemSemanticEditPolicy
, which is the semantic edit policy
installed on the diagram edit part of the generated editor, should be installed on the Freeform diagram
edit part for a unique role (do not install it over the
SEMANTIC_ROLE
, instead create a new unique role). An extension is used so that additional checks can be implemented
to ensure that the edit policy does not interfere with Model RealTime code.
public class EcoreEditPolicyProvider extends AbstractProvider implements IEditPolicyProvider { public void createEditPolicies(EditPart editPart) { if (editPart instanceof DiagramEditPart) { DiagramEditPart dep = (DiagramEditPart)editPart; if (UMLDiagramKind.FREEFORM_LITERAL.getName().equals(dep.getNotationView().getType())) { dep.installEditPolicy("ECORE_DIAGRAM_SEMANTIC_ROLE", new DiagramEPackageItemSemanticEditPolicy()); } } } public boolean provides(IOperation operation) { if (operation instanceof CreateEditPoliciesOperation) { EditPart ep = ((CreateEditPoliciesOperation) operation).getEditPart(); if (ep instanceof DiagramEditPart) { DiagramEditPart dep = (DiagramEditPart)ep; return UMLDiagramKind.FREEFORM_LITERAL.getName().equals(dep.getNotationView().getType()); } } return false; } }
In this example, the DiagramEPackageItemSemanticEditPolicy
class extends EPackageItemSemanticEditPolicy
.
This class performs a check to see if the edit helper context of the request is part of the generated editor.
To do this, obtain the edit helper contexts' corresponding element type by querying the
ElementTypeRegistry
and use the generated utility XXXElementTypes.isKnownElementType(IElementType elementType)
to check if the element type comes from the generated editor. If the check is successful, then return the command from the super class.
Otherwise no semantic command should be provided; return null.
public class DiagramEPackageItemSemanticEditPolicy extends EPackageItemSemanticEditPolicy { protected Command getSemanticCommand(IEditCommandRequest request) { IEditCommandRequest completedRequest = completeRequest(request); Object editHelperContext = completedRequest.getEditHelperContext(); if (editHelperContext instanceof View || (editHelperContext instanceof IEditHelperContext && ((IEditHelperContext) editHelperContext).getEObject() instanceof View)) { return null; } if (editHelperContext == null) { editHelperContext = ViewUtil.resolveSemanticElement((View) getHost().getModel()); } IElementType elementType = ElementTypeRegistry.getInstance().getElementType(editHelperContext); if (EcoreElementTypes.isKnownElementType(elementType)) { return super.getSemanticCommand(request); } return null; } }
When creating elements on a generated diagram editor surface, the context for creation is the
semantic element of the diagram. The diagram semantic element can contain all top level elements,
in the case of the Ecore editor, the diagram semantic element is an EPackage
.
When creating elements on a Freeform diagram, the diagram itself does not have a semantic element, but
if it did, chances are that it would not be able to contain the top level elements of the generated editor.
Therefore when creating such elements on a Freeform diagram, edit helpers are utilized to switch the
edit context from the diagram to a new semantic element which can contain the element.
To do this, change the generated XXXBaseEditHelper
to extend
ContextEditHelper
. This class,
along with a bit of custom implementation, will switch the context from the diagram to a proper context.
For the TODO in the
ContextEditHelper
, implement a mechanism to obtain an element which can contain the element to be created.
Such a mechanism could be as simple as creating an annotation on the diagram with a root element, to
something more elaborate such as a dialog which will allow the client to select an existing element in the workspace.
The type of the context element should be the same type as the generated diagrams' semantic element.
public class EcoreBaseEditHelper extends ContextEditHelper { ... }
public class ContextEditHelper extends AbstractEditHelper { protected ICommand getEditContextCommand(GetEditContextRequest req) { GetEditContextCommand result = null; IEditCommandRequest editRequest = req.getEditCommandRequest(); if (editRequest instanceof CreateElementRequest) { CreateElementRequest createRequest = (CreateElementRequest) editRequest; EObject container = createRequest.getContainer(); if (container instanceof Diagram) { final Diagram diagram = (Diagram) container; if (UMLDiagramKind.FREEFORM_LITERAL.getName().equals(diagram.getType())) { EObject myContext; // TODO for the client // search for or create the container element // and set it as the editContext result = new GetEditContextCommand(req); createRequest.setContainer(myContext); result.setEditContext(myContext); } } } return result; } }
The generated element types need to be bound to the
com.ibm.xtools.uml.type.context
context in order to function on the Freeform diagram. Duplicate the generated elementTypeBindings
extension in the plugin.xml
, remove the clientContext
and
set the binding context
to be com.ibm.xtools.uml.type.context
.
The end result should be similar to this example:
<extension point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings"> <binding context="com.ibm.xtools.uml.type.context"> <elementType ref="org.eclipse.gmf.ecore.editor.EPackage_1000"/> <elementType ref="org.eclipse.gmf.ecore.editor.EAttribute_3001"/> ... </binding> </extension>
In each top level generated view factory, the shortcut annotation is added to the view created on the Freeform diagram because the Freeform diagram type does not match the model ID of the generated editor. To prevent adding the shortcut annotation to generated views on the Freeform diagram, add an additional check comparing the container view type to the Freeform diagram type for each generated view factory. This will ensure that elements created on the Freeform diagram surface will not get the shortcut annotation, thereby preventing the installation of the shortcut decoration.
protected void decorateView(View containerView, View view, IAdaptable semanticAdapter, String semanticHint, int index, boolean persisted) { ... if (!EPackageEditPart.MODEL_ID.equals(EcoreVisualIDRegistry.getModelID(containerView)) && !UMLDiagramKind.FREEFORM_LITERAL.getName().equals(containerView.getType())) { EAnnotation shortcutAnnotation = EcoreFactory.eINSTANCE.createEAnnotation(); shortcutAnnotation.setSource("Shortcut"); shortcutAnnotation.getDetails().put("modelID", EcoreEditPart.MODEL_ID); view.getEAnnotations().add(shortcutAnnotation); } ... }
Generated editors create multiple layers when configuring the generated editor in the
XXXDiagramEditor#configureGraphicalViewer()
method. A layer is a transparent figure intended
to be added exclusively to a
LayeredPane
, who has the responsibility of managing its layers. These layers are important to the
generated editor because they support features such as external labels. Therefore it is necessary to create
the same layers to the Freeform diagram that the generated editor creates.
Note: It is not recommended to add these layers to RMP UML Modeler diagrams because they could potentially cause problems. If external node labels are not being used at all, then do not add these layers.
For each edit part which contains child external node labels, locate the getExternalLabelsContainer()
method.
Modify the method implementation such that
if the external node labels layer cannot be found, then create and add the new layer.
To create the layer, simply
copy the contents of the XXXDiagramEditor#configureGraphicalViewer()
method,
which creates the external node labels layer, from the generated code. The resulting method should
resemble the following:
/** * @generated NOT */ protected IFigure getExternalLabelsContainer() { LayerManager root = (LayerManager) getRoot(); Layer extLabelsLayer = (Layer)root.getLayer(EcoreEditPartFactory.EXTERNAL_NODE_LABELS_LAYER); if (extLabelsLayer == null) { LayeredPane printableLayers = (LayeredPane) root.getLayer(LayerConstants.PRINTABLE_LAYERS); extLabelsLayer = new FreeformLayer(); extLabelsLayer.setLayoutManager(new DelegatingLayout()); printableLayers.addLayerAfter(extLabelsLayer, EcoreEditPartFactory.EXTERNAL_NODE_LABELS_LAYER, LayerConstants.PRIMARY_LAYER); } return extLabelsLayer; }