Element types are used by the UML Modeler to describe the types of UML elements that can be created, modified and deleted through the application. Each element type defines the editing behaviour for the model objects that it describes, as well as its name and icon for display in the UI.
In the UML Modeler, editing operations on some kinds of elements are complex. For example, when a UML element is created it is often assigned a default name and sometimes other related elements are also created, such as a collaboration and an interaction to provide the context for a new sequence diagram. Likewise, when a UML element is deleted from a model, some related elements are also deleted, such as an association when one of its member properties is deleted.
These complex editing operations are implemented by extensible commands, using the GMF Element Type API. The UML Modeler enriches common editing commands for many UML element types by extending them with "advice" comprising additional commands that are automatically composed with the basic editing commands such as element creation and deletion.
This section describes how to provide extensions to the UML Modeler editing commands by adding custom element types and contributing edit advice. The Modifying Models topic illustrates how to take advantage of these extensible commands to provide the same rich editing experience as the UML Modeler, and the Customizing Tools topic describes how to use custom element types in the diagram tool palette.
The Graphical Modeling Framework provides the API to define element types through the
org.eclipse.gmf.runtime.emf.type.core.elementTypes
extension point. The UML Modeler defines
IMetamodelType
s
that correspond to the types in the UML metamodel and provides basic
editing commands for UML model elements. It also provides
ISpecializationType
s
that describe more complex elements. For example, the URL type is a specialization
of the UML comment type and describes comment that is stereotyped as a URL. The following
diagram shows how element types are related to the types that define their editing behaviour:
A metamodel type uses an IEditHelper
to define the basic editing
commands for elements of its type. A specialization type uses an
IEditHelperAdvice
to define the additional editing commands
that are required to extend this basic editing behaviour.
The following diagram illustrates how the command is constructed to edit a model object that matches a specialization type. The command will be a composite containing first the 'before' command from the specialization type's advice, followed by the basic edit command from the edit helper, followed by 'after' command from the specialization:
Advice can also be bound to any existing element type to contribute
commands before or after the basic editing command. Element types and
advice declared through the
org.eclipse.gmf.runtime.emf.type.core.elementTypes
extension point are registered in GMF's
ElementTypeRegistry
.
The property view will automatically list new element types in its pick lists to create new elements. These appear in cell editors on the advanced properties tab and in the property page dialog (accessed from the property pages action on the property view toolbar).
To illustrate extending the UML Modeler UI, consider adding an element type to be used from a diagram palette tool or a menu item to create new abstract UML classes. The following extension is declared to register a specialization of the UML class element type (whose ID is com.ibm.xtools.uml.class):
<extension
point="org.eclipse.gmf.runtime.emf.type.core.elementTypes">
<specializationType
id="com.ibm.examples.abstractClass"
name="%Abstract_Class"
icon="icons/abstractClass.gif"
edithelperadvice="com.ibm.examples.AbstractClassAdvice">
<specializes id="com.ibm.xtools.uml.class"/>
<enablement>
<test
property="com.ibm.examples.isAbstractClass"
value="true"/>
</enablement>
</specializationType>
</extension>
The enablement expression (org.eclipse.core.expressions) uses a property tester to match model objects that are abstract classes so that the extended editing behaviours will be applied to them. The AbstractClassAdvice will configure new classes of this type to be abstract, as follows:
public class AbstractClassAdvice extends AbstractEditHelperAdvice { protected ICommand getAfterConfigureCommand(final ConfigureRequest request) { return new ConfigureElementCommand(request) { protected CommandResult doExecuteWithResult( IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { // get the new class Class abstractClass = (Class) request.getElementToConfigure(); // make it abstract abstractClass.setIsAbstract(true); return CommandResult.newOKCommandResult(); } }; } }
Element types and advice are organized by client context. In order for the new element type for abstract classes to participate in the UML Modeler application it must be bound to the UML Modeler client context, like this:
<extension
point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings">
<binding context="com.ibm.xtools.uml.type.context">
<elementType ref="com.ibm.examples.abstractClass"/>
</binding>
</extension>
Now the property view will automatically list the type for abstract class in its pick lists to create new elements. For example, it will appear in the list of types that can be created in the NestedClassifier feature on the property page dialog (accessed from the property pages action on the property view toolbar).
To illustrate extending existing UML Modeler editing behaviour, consider binding edit advice that will apply a stereotype to a UML part when its type is also stereotyped. The following extension is declared to register the edit helper advice:
<extension
point="org.eclipse.gmf.runtime.emf.type.core.elementTypes">
<metamodel
nsURI="http://www.eclipse.org/uml2/3.0.0/UML">
<adviceBinding
id="com.ibm.examples.xyz.XyzPartAdvice"
typeId="com.ibm.xtools.uml.part"
class="com.ibm.examples.xyz.XyzPartAdviceBinding">
</adviceBinding>
</metamodel>
</extension>
Suppose there is a profile for the XYZ domain (XyzProfile) that contains a stereotype for UML classes (XyzClass) and a stereotype for UML parts (XyzPart). When a new part is created whose type is an XyzClass, the XyzPartAdviceBinding class will contribute a command that applies the XyzPart stereotype to the new part, as follows:
public class XyzPartAdviceBinding extends AbstractEditHelperAdvice { protected ICommand getAfterConfigureCommand(final ConfigureRequest request) { return new ConfigureElementCommand(request) { protected CommandResult doExecuteWithResult( IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { // get the new part Part part = (Part) request.getElementToConfigure(); // check its type Type type = part.getType(); if (type != null) { Stereotype classStereotype = type.getAppliedStereotype("XyzProfile::XyzClass"); //$NON-NLS-1$ if (classStereotype != null) { Stereotype partStereotype = part.getApplicableStereotype("XyzProfile::XyzPart"); // $NON-NSL-1$ if (partStereotype != null) { // apply the part stereotype part.applyStereotype(partStereotype); } } } return CommandResult.newOKCommandResult(); } }; } }
Again, element types and advice are organized by client context. In order for the new advice binding to participate in the UML Modeler application it must be bound to the UML Modeler client context, like this:
<extension
point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings">
<binding context="com.ibm.xtools.uml.type.context">
<advice ref="com.ibm.examples.xyz.XyzPartAdvice"/>
</binding>
</extension>
The element type registry can be used to retreive element types that have been registered in the UML Modeler. For example, the following will retrieve the abstract class type from the registry using its unique identifier:
ElementTypeRegistry registry = ElementTypeRegistry.getInstance();
IElementType abstractClassType = registry.getType("com.ibm.examples.abstractClass
");
The registry can also find the element types that describe the kinds of model objects that can be created in another model object. For example, here is a method to create a menu with actions that create children in a specific feature:
private void fillMenu(Menu menu, EObject container, EReference feature) { // fills a menu with items that create model objects in the // specified feature of the container IElementType type = ElementTypeRegistry.getInstance().getElementType(container); IEditHelper helper = type.getEditHelper(); List values = helper.getContainedValues(container, feature); if (values != null) { for (Iterator i = values.iterator(); i.hasNext();) { Object nextValue = i.next(); if (nextValue instanceof IElementType) { IElementType next = (IElementType) nextValue; Action action = new CreateElementAction(container, feature, next); addActionToMenu(menu, action); } } } }
Here, the CreateElementAction
uses the
UMLElementFactory
to create the new element, as follows:
public class CreateElementAction extends AbstractModelActionHandler { protected TransactionalEditingDomain getEditingDomain() { return UMLModeler.getEditingDomain(); } protected void doRun(IProgressMonitor progressMonitor) { UMLElementFactory.createElement(getContainer(), getElementType(), getFeature(), progressMonitor); } }