Visualizing mapped elements onto UML diagrams

This section explains how to show domain elements as UML model elements on a diagram.

UI Objects

Before understanding how to visualize objects onto diagrams, we must first familiarize ourselves with the concept of UI objects and distinguish them from domain elements. UI objects are objects that the user can access directly from the user interface. An Eclipse user might never be able to directly interact with a domain element through the interface, but instead must interact with some visual representation of that object. For instance, a user may never be able to click on an IPluginBase, but the user can click on an IFile such as plugin.xml. The IFile is an UI object in this case because it provides a user accessible representation of the IPluginBase.

IMMIUIProvider and IMMIUIHandler

To show the ITarget EObjects on a diagram, we need to implement an UI provider. The UI provider defines the behavior that occurs when an open action is performed on the visual representation of the mapped element (typically through double clicking the shape on the diagram surface). It also defines a method to return an IMMIUIHandler. The IMMIUIHandler has three purposes.

  1. It defines a method that returns the default diagram path for a given UI object (such as IFile) and a given UI context (such as the type of diagram the IFile would be added to).
  2. It defines a method that returns source elements for a given UI object and a given UI context.
  3. It defines a method that returns the EClass that the target should use for a given UI context .

We will elaborate on each of these three methods later, but first, we need to understand what an UI context is.

What is an UI context?

It can be important to distinguish the context in which an UI object is selected. For example, we may wish to respond differently depending on whether an UI object was selected while a UML Class Diagram was open or while a UML Sequence Diagram was open. The IUIContext allows us to make such a distinction.

The concept of an UI context has been chosen since it is extensible. It is simply a set of key and value pairs. For example, an edit part, such as a diagram edit part, may be used as the UI context. Some possible UI contexts keys have been defined in the IUIContextConstants interface.

Returning the default diagram path

Suppose you wish to create a new diagram for a particular UI object. Determining the default location of the diagram to be created will prove to be a requirement. If the UI object happens to be an IFile, we can typically determine the path based on the location of the IFile.

The default diagram path will become particularly useful when implementing the "Add to New Diagram" context menu item, which we'll learn how to do at the end of this section.

Returning source elements and EClass

When an UI object is selected, we need to obtain the underlying domain element(s). For example, if the user selects an IFile, we may want to return an IPluginBase domain element. Once we have the domain element, we can determine what EClass it corresponds to based on the type of diagram that the domain element will be added to.

For example, we may wish to visualize the IPluginBase as a UML Artifact when it's added to a UML Class Diagram, but we would like to visualize it as a UML State when it's added to a UML State Machine Diagram. We would check the UI context and return the EClass that corresponds to a UML Class or a UML State, as appropriate.

Example

Registering the IMMIUIProvider

In this example, the IMMIUIProvider provides for the IFile UI object. It is enabled for certain UI contexts, and the enablement is defined by standard Eclipse enablement practices such as through a property tester. The DiagramTypePropertyTester used in this example tests if the IUIContextConstants.DIAGRAM_TYPE UI context is one of Freeform, Class, or Deployment. The example also defines an open handler that handles the open action of a plugin object.

<extension
        id="pdeumlvizuiprovider"
        name="MMI UI Provider for PDE"
        point="com.ibm.xtools.mmi.ui.MMIUIProviders">
    <MMIUIProvider class="com.ibm.xtools.umlviz.ui.examples.pde.internal.ui.providers.PdeUmlVizUIProvider">
        <Priority name="Medium"/>
        <MMIUIHandler
            name="PdeVizUIHandler"
                uiObjectClass="org.eclipse.core.resources.IFile">
            <enablement>
                <or>
                    <test property="com.ibm.xtools.mmi.ui.DiagramTypePropertyTester" value="Freeform"/>
                    <test property="com.ibm.xtools.mmi.ui.DiagramTypePropertyTester" value="Class"/>
                    <test property="com.ibm.xtools.mmi.ui.DiagramTypePropertyTester" value="Deployment"/>
                </or>
            </enablement>
        </MMIUIHandler>
        <OpenStructuredReferenceHandler name="PdeVizOpenHandler">
            <StructuredReferenceProviderId id="pde.IPluginBase"/>
        </OpenStructuredReferenceHandler>
    </MMIUIProvider>
</extension>

Implementing the IMMIUIProvider

The UI provider below provides for GetUIHandlerOperation operations. To match the XML, it provides for IFile UI objects. It adds the restriction that the IFile's filename must be plugin.xml or manifest.mf.

An implementation of an IMMIUIProvider is shown below.

public class PdeUmlVizUIProvider extends AbstractProvider
    implements IMMIUIProvider {

    /**
     * @see com.ibm.xtools.mmi.ui.services.IMMIUIProvider#getUIHandler(java.lang.Object, com.ibm.xtools.mmi.ui.util.IUIContext)
     */
    public IMMIUIHandler getUIHandler(Object uiObject, IUIContext uiContext) {
        return PdeVizUIHandler.getInstance();
    }

	/**
	 * @see com.ibm.xtools.mmi.ui.services.IMMIUIProvider#getStructuredReferenceOpenHandler(java.lang.String)
	 */
    public IStructuredReferenceOpenHandler getStructuredReferenceOpenHandler(String handlerID) {
        return PdeStructuredReferenceOpenHandler.getInstance();
    }

    /**
     * @see org.eclipse.gmf.runtime.common.core.service.IProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation)
     */
    public boolean provides(IOperation operation) {
        if (operation instanceof GetUIHandlerOperation) {
            GetUIHandlerOperation uiHandlerOp =
            	(GetUIHandlerOperation) operation;
            Object uiObject = uiHandlerOp.getUIObject();
            if (uiObject instanceof IFile) {
                IFile file = (IFile) uiObject;
                String fileName = file.getName().toLowerCase();
                if (fileName.equals("plugin.xml") ||
                	fileName.equals("manifest.mf")) {
                    return true;
                }
            }
            return false;
        }
        return true;
    }
}

IMMIUIHandler

The UI provider also specifies an IMMIUIHandler. As explained, the UI handler is responsible for three tasks.

  1. Returning source elements given an UI object (and a context). In our example, we return the plugin domain element given an IFile.
    public List getSourceElements(Object referencedContext, Object uiObject,
    	IUIContext uiContext) {
    	
        IPluginBase plugin = getPlugin(uiObject);
        if(plugin != null) {
            return Collections.singletonList(plugin);
        }
        return null;
    }
    
  2. Returning the EClass given an object from the source domain (and a context). In our example, for source domain objects representing plugins, we return the Artifact EClass.
    public EClass getTargetEClass(Object referencedContext, Object sourceObject,
        IUIContext uiContext) {
    
        String diagramType = (String)uiContext.getContextInfo(IUIContextConstants.DIAGRAM_TYPE);
    
        if(diagramType.equals(UMLDiagramKind.DEPLOYMENT_LITERAL.getName()) ||
            diagramType.equals(UMLDiagramKind.FREEFORM_LITERAL.getName()) ||
            diagramType.equals(UMLDiagramKind.CLASS_LITERAL.getName())) {
            if(sourceObject instanceof IPluginBase) {
                return UMLPackage.eINSTANCE.getArtifact();
            }
        }
        return null;
    }
    
  3. Returning a default diagram path given an UI object (and a context). In our example, for IFile objects representing plugins, we return the project that contained the IFile.
    public IPath getDefaultDiagramPath(Object uiObject, IUIContext uiContext) {
        IFile file = getPluginFile(uiObject);
        if(file != null) {
            return file.getProject().getFullPath();
        }
        return null;
    }
    

IStructuredReferenceOpenHandler

Finally, the UI provider also binds itself to an open handler for StructuredReference objects. A class that implements the IStructuredReferenceOpenHandler interface needs to handle the open action for StructuredReference objects. For example, the open handler is invoked when double clicking a mapped model element that has been visualized onto the diagram surface. Typically, the structured reference is resolved to the source element, and an action is performed on the resolved element. In our example, we open the manifest editor on the resolved domain element.

The code below shows how to resolve the domain element and open the plugin manifest editor.

Object sourceElement = StructuredReferenceService.resolveToDomainElement(
    referencedContext, sRef);
if(sourceElement instanceof IPluginBase) {
    IPluginBase pluginBase = (IPluginBase) sourceElement;
    ManifestEditor.open(pluginBase, false);
}

UI actions

UI objects can be visualized on the diagram surface in one of three common ways. The first is by dragging and dropping the UI object onto the diagram surface. The second is by adding a pop up context menu on the UI object. The last is by creating the element using the diagram editor's palette.

Drag and drop

For example, the XML below illustrates how to enable dragging and dropping from the Java Package Explorer.

<extension
    id="pdeumldragdropProvider"
    name="Drag and drop Provider for PDE Visualization"
    point="org.eclipse.gmf.runtime.common.ui.services.dnd.dragDropListenerProviders">
    <DragDropListenerProvider
        class="com.ibm.xtools.mmi.ui.dnd.MMIDragDropTargetListener"
        id="com.ibm.xtools.umlviz.ui.examples.pde.pdedndlistenerprovider">
    <Priority name="Low"/>
        <ViewId id="UMLVisualizerEditor">
            <ElementType
                class="org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart">
                <OperationType operation="drop">
                    <TransferId transferId="JDTLocalSelectionTransfer"/>
                </OperationType>
            </ElementType>
         </ViewId>
    </DragDropListenerProvider>
</extension>

No further code is required in addition to the XML. Simply registering the MMIDragDropTargetListener as the DragDropListenerProvider completely handles dragging and dropping onto the diagram. Be sure to change the UMLVisualizerEditor editor ID if you have built your own domain specific editor.

Pop up menus

As another example, we can identify the UI object in the XML and define a pop up menu for it.

The following XML contributes two menu items, one for adding to the current diagram editor, and another to add the selected element onto a new UML Class Diagram. The code that contains the functionality for adding the source element represented by the UI object onto the current open diagram is handled completely by MMI. To contribute an action that adds the selected element to the current diagram, all you need to do is to add the relevant XML, shown below, to your plugin. No further code is required.

   <extension
         id="PDEPopupMenus"
         name="PDE Popup Menus"
         point="org.eclipse.ui.popupMenus">
		<!-- Add to Current/Create new -->
      <objectContribution
            objectClass="org.eclipse.core.resources.IFile"
            nameFilter="plugin.xml"
            id="pluginVisualizationPopup">
         <action
               label="&Class diagram"
               class="com.ibm.xtools.umlviz.ui.examples.pde.internal.actions.AddToNewClassDiagramAction"
               menubarPath="com.ibm.xtools.umlviz.ui.VizMenu/addToNewDiagramFileMenu/diagramsGroup"
               id="addToNewClassDiagramAction">
         </action>
         <action
               label="Add to &Current Diagram"
               class="com.ibm.xtools.mmi.ui.actions.AddToCurrentDiagramAction"
               menubarPath="com.ibm.xtools.umlviz.ui.VizMenu/addToGroup"
               id="addToCurrentDiagramAction">
         </action>
      </objectContribution>
   </extension>

To contribute an action that adds to a new class diagram, we need to add some code.

public class AddToNewClassDiagramAction
    extends AbstractAddToNewDiagramAction {

    private static final IUIContext context = new UIContext(
        IUIContextConstants.DIAGRAM_TYPE, UMLDiagramKind.CLASS_LITERAL.getName()); 

    protected IUIContext getUIContext() {
        return context;
    }    

    public IPath getDefaultDiagramPath(List elements, String diagramType) {
        //assume only 1 element
        PdeVizUIHandler handler = PdeVizUIHandler.getInstance();
        return handler.getDefaultDiagramPath(elements.get(0),
            new UIContext(IUIContextConstants.DIAGRAM_TYPE, diagramType));
    }

    protected TransactionalEditingDomain getEditingDomain() {
        return TransactionalEditingDomain.Registry.INSTANCE
            .getEditingDomain(
                "org.eclipse.gmf.runtime.emf.core.compatibility.MSLEditingDomain");
    }

    protected String getUniqueFileName(
        final IPath containerPath,
            String filename,
            String extensionWithDot) {
        //increment the number appended to the filename to obtain a unique filename
        ...
    }

    protected DiagramEditPart createDiagramEditPart(List targetEl,
        IProgressMonitor progressMonitor) {

        IPath containerPath = getDefaultDiagramPath(targetEl,
            UMLDiagramKind.CLASS_LITERAL.getName());

        if (containerPath != null) {
            String fileName = "pdeclassdiagram";
            fileName = getUniqueFileName(
                containerPath, fileName, StringStatics.PERIOD + "dnx");

            IResource diagramFile = MMIUIUtil.createAndOpenDiagram(
                getEditingDomain(),
                containerPath,
                fileName,
                StringStatics.PERIOD +
                	PropertiesConfigurationManager.getString(
                		"com.ibm.xtools.umlviz.ui", "DIAGRAM_EXT"),
                new ByteArrayInputStream(new byte[0]),
                UMLDiagramKind.CLASS_LITERAL.getName(),
                getWindow(),
                progressMonitor,
                true,
                false);

            if (diagramFile != null) {
                return getOpenedDiagramEditPart();
            }
        }

        return null;
    }
}

In the code above, we implemented the getUIContext() method to return an UI context that specifies the diagram type will be the UML Class Diagram.

new UIContext(IUIContextConstants.DIAGRAM_TYPE,
    UMLDiagramKind.CLASS_LITERAL.getName()); 

Then, we used the getDefaultDiagramPath() method defined in the the IMMIUIHandler interface to obtain the default diagram path. Typically, the action iterates through the list of elements, consulting the handler each time to check if a default diagram path exists for that element. Once a default diagram path can be found, it is returned. To make the example easier to understand, we simply check the first element in the list and assume a valid diagram path can be obtained.

The editing domain used here is the compatibility editing domain, which is defined by the constant org.eclipse.gmf.runtime.emf.core.compatibility.MSLEditingDomain. This is the default editing domain used by the UMLVisualizerEditor.

Lastly, in the createDiagramEditPart() method, we use the MMIUIUtil.createAndOpenDiagram() utility to create and open the diagram editor.


Legal notices