This section describes the necessary steps to integrate a domain specific model editor into the RMP UML Modeler. The result will allow for the creation of a custom diagram which will be stored inside an EMX model file. The domain model corresponding to the diagram can be stored in a choice of either the EMX model file or a separate resource file.
The first step is to create a custom diagram type. The steps are outlined in
Adding diagram types. The only part that needs to be done differently is the implementation of the
AddDiagramCommand
.
Depending on whether the domain model will reside inside the EMX
model file or within a separate resource file
the implementation of the
AddDiagramCommand#getDiagramElement()
method will differ.
First lets take a look at how the implementation of the
AddDiagramCommand#getDiagramElement()
method will look like if the domain model is to reside
inside the EMX model file. In this case, the domain model will need to be contained inside an annotation.
A new annotation and domain root element will need to be created.
public class AddDiagramCommand extends AbstractTransactionalCommand { private final Namespace namespace; ... // new private static final String DOMAIN_ANNOTATION = "myDomain"; ... private EObject getDiagramElement() { // new EAnnotation annotation = UML2Util.getEAnnotation(namespace, DOMAIN_ANNOTATION, true); EObject root = <your-domain-factory>.eINSTANCE.createModel(); annotation.getContents().add(root); return root; } ... }
Next lets take a look at how the implementation of the
AddDiagramCommand#getDiagramElement()
method will look like if the domain model is to reside
in a separate resource file. In this case, a new file and domain root element will need to be created.
public class AddDiagramCommand extends AbstractTransactionalCommand { private final Namespace namespace; ... // new private static final String DEFAULT_FILE_NAME = "default"; private static final String EXT = "myExt"; ... protected EObject getDiagramElement() { // new IContainer container = ((IFile)getWorkspaceFiles(namespace).get(0)).getParent(); // find a unique file name IFile newFile = container.getFile(new Path(DEFAULT_FILE_NAME + "." + EXT)); for (int count = 1; newFile.exists(); ++count) { newFile = container.getFile(new Path(DEFAULT_FILE_NAME + count + "." + EXT)); } // create the domain resource TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(namespace); Resource resource = domain.getResourceSet().createResource( URI.createPlatformResourceURI(newFile.getFullPath().toString())); EObject root = <your-domain-factory>.eINSTANCE.createModel(); resource.getContents().add(root); try { resource.save(Collections.EMPTY_MAP); } catch (IOException e) { // TODO log } return root; } ... }
Since the domain model resides in a separate resource file, when the diagram resource is saved,
the domain model resource also needs to be saved. To do this create a
org.eclipse.emf.transaction.ResourceSetListener
(see
Listening to model changes for more information).
The org.eclipse.emf.transaction.NotificationFilter
is constructed to listen only for notification from the
Resource
class, for
SET
and
UNSET
events of the
RESOURCE_IS_MODIFIED
feature.
In the resourceSetChanged
method,
sift through the notifications to determine if any of the notifying resources has been saved. If a resource has been saved,
find all of its resource imports using the
org.eclipse.gmf.runtime.emf.core.util.CrossReferenceAdapter
, and save any resource import which has the expected
domain model file extension if required.
public class SavingResourceSetListener extends ResourceSetListenerImpl { private static SavingResourceSetListener instance = null; private static final String EXT = "myExt"; private SavingResourceSetListener() { // private constructor } public static SavingResourceSetListener getInstance() { if (instance == null) { instance = new TestRSL(); } return instance; } public NotificationFilter getFilter() { return NotificationFilter .createNotifierTypeFilter(Resource.class).and( NotificationFilter.createEventTypeFilter(Notification.SET).or( NotificationFilter .createEventTypeFilter(Notification.UNSET))).and( NotificationFilter.createFeatureFilter(Resource.class, Resource.RESOURCE__IS_MODIFIED)); } public void resourceSetChanged(ResourceSetChangeEvent event) { for (Iterator i = event.getNotifications().iterator(); i.hasNext(); ) { Notification notification = (Notification) i.next(); Resource notifierResource = (Resource) notification.getNotifier(); if (notification.getNewBooleanValue() == false && notification.getOldBooleanValue() == true) { EList contents = notifierResource.getContents(); if (!contents.isEmpty()) { Object root = contents.get(0); if (root instanceof EObject && ((EObject) root).eResource() != null && ((EObject) root).eResource().equals(notifierResource) && notifierResource.isLoaded()) { CrossReferenceAdapter cra = CrossReferenceAdapter.getExistingCrossReferenceAdapter(notifierResource); if (cra != null) { Set imports = cra.getImports(notifierResource); for (Iterator i2 = imports.iterator(); i2.hasNext(); ) { Resource import_ = (Resource)i2.next(); if (import_.getURI().fileExtension() != null && import_.getURI().fileExtension().equals(EXT) && import_.isLoaded() && import_.isModified()) { try { import_.save(Collections.EMPTY_MAP); } catch (IOException e) { // TODO log error } } } } } } } } } }
Now that the diagram and domain model are created, the final steps are to add customizations and tooling to the editor.
See the sections under Customizing the user interface and Customizing diagrams for more information.