Integrating domain models

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.


Legal notices