Non-Modeled Intermediary View Objects (henceforth NMIVO__s) are objects that provide a view for containment references between __EObjects in an EMF model without being associated with corresponding EMF model objects themselves. They are created in the ItemProvider of an EObject that has an "ownership" role in the containment reference. NMIVOs must implement all methods defined in a modelled object ItemProvider's interfaces. Typically, they are subclasses of ItemProviderAdapter implemented as a subclass of a user-defined TransientItemProvider.
[TOC]
ParentObject: the EObject that has an "ownership" role in the containment reference (Supplier in 19.2.3 of the EMF book)
ChildObject: the EObject that has an "owned-by" role in the containment reference (PurchaseOrder and Customer)
ParentItemProvider: the ItemProvider associated with the ParentObject (SupplierItemProvider)
ChildItemProvider: the ItemProvider associated with the ChildObject (PurchaseOrderItemProvider and CustomerItemProvider)
TransientItemProvider: the parent class of the NMIVOItemProviders (TransientSupplierItemProvider)
NMIVOItemProvider: the NMIVO (OrdersItemProvider and CustomersItemProvider)
Before adding the NMIVOs, make the following changes to the EMF Generator Model (.genmodel) and regenerate the Edit code:
ParentObject, change Edit:Provider Type to StatefulParentObject, and for each ChildObject change Edit:Create Child to False. This prevents the editor from allowing the user to create children from the ParentObject.ParentItemProviderThe following class variables will be created:
children (type List)The following methods will be added:
getFirstNMIVOItemProvider()getSecondNMIVOItemProvider()ItemProviderAdapter:getChildren()createRemoveCommand()createAddCommand()createWrappedCommand()dispose()ParentItemProviderprotected List children = null;
@Override
public Collection getChildren(Object object)
{
if (children == null) {
ParentObject parentObject = (ParentObject)object;
children = new ArrayList();
/* Here we add the NMIVOs */
children.add(new FirstNMIVOItemProvider(adapterFactory, parentObject));
children.add(new SecondNMIVOItemProvider(adapterFactory, parentObject));
...
}
return children;
}
/* These are the accessors for the NMIVOs that will
* be used thoughout parts of the affected code. Be
* sure to add a ``null'' check to avoid NullPointerExceptions
* for the runtime situation where getChildren() has not been
* called yet.
*/
public Object getFirstNMVIO()
{
return children != null ? children.get(0) : null;
}
public Object getSecondNMIVO()
{
return children != null ? children.get(1) : null;
}
...
ParentObject commands /**
* Creates a remove command that is wrapped to return the correct non-modeled item,
* in place of the target ParentObject, as part of the affected objects.
*/
@Override
protected Command createRemoveCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, Collection<?> collection)
{
return createWrappedCommand(super.createRemoveCommand(domain, owner, feature, collection), owner, feature);
}
/**
* Creates an add command that is wrapped to return the correct non-modeled item,
* in place of the target ParentObject, as part of the affected objects.
*/
@Override
protected Command createAddCommand(EditingDomain domain, EObject owner, EStructuralFeature feature,
Collection<?> collection, int index)
{
return createWrappedCommand(super.createAddCommand(domain, owner, feature, collection, index), owner, feature);
}
/**
* Returns a wrapper for the given command that returns the correct non-modeled item,
* in place of the target ParentObject, as part of the affected objects.
* AppPackage corresponds to EMF application package (EPO1Package in the book)
* PARENTOBJECT__FIRST_NMIVO, PARENTOBJECT__SECOND_NMIVO, etc. correspond to the names of the NMIVOs
*/
protected Command createWrappedCommand(Command command, final EObject owner, final EStructuralFeature feature)
{
if (feature == AppPackage.Literals.PARENTOBJECT__FIRST_NMIVO ||
feature == AppPackage.Literals.PARENTOBJECT__SECOND_NMIVO ||
... )
{
return
new CommandWrapper(command)
{
@Override
public Collection<?> getAffectedObjects()
{
Collection<?> affected = super.getAffectedObjects();
if (affected.contains(owner))
{
affected = Collections.singleton(
feature == AppPackage.Literals.PARENTOBJECT__FIRST_NMIVO ? getFirstNMIVO() :
feature == AppPackage.Literals.PARENTOBJECT__SECOND_NMIVO ? getSecondNMIVO() :
...
getLastNMIVO() // The `else' clause of a chained ternary operator
);
}
return affected;
}
};
}
return command;
}
Since the NMIVOs were user-defined, they must be explicitly disposed of:
/**
* Make sure the transient objects are destroyed along
* with the enumeration. Any time you add ItemProviders
* you need to dispose of them yourself.
*/
@Override
public void dispose()
{
super.dispose();
if (children != null) {
for (Object child : children) {
((IDisposable)child).dispose();
}
}
}
ChildItemProviderThe following methods will be overriden:
getParent() /**
* Returns the NMIVO as the parent of this modeled child object
**/
@Override
public Object getParent(Object object)
{
Object parentObject = super.getParent(object);
ParentItemProvider parentItemProvider = (ParentItemProvider)adapterFactory.adapt(parentObject, IEditingDomainProvider.class);
/* getChildNMIVO() corresponds to getCustomers() or getOrders() in the book */
return parentItemProvider != null ? parentItemProvider.getChildNMIVO() : null;
}
TransientItemProviderThe following methods will be overriden from ItemProviderAdapter:
TransientSupplierItemProvider() (constructor)getChildren()getParent()getNewChildDescriptors()getImage()getResourceLocator()createCommand()createAddCommand()createWrappedCommand()TransientItemProvider implementationpublic class TransientItemProvider
extends ItemProviderAdapter
implements
IEditingDomainProvider,
IStructuredContentProvider,
ITreeItemContentProvider,
IItemLabelProvider,
IItemPropertySource,
ITableItemContentProvider // if Table Providers is True
{
public TransientItemProvider(AdapterFactory adapterFactory, ParentObject parentObject)
{
/* Since the NMIVO was not added to the list of observers
we must add it explicitly */
super(adapterFactory);
parentObject.eAdapters().add(this);
}
@Override
public Collection getChildren(Object object)
{
/* Since the NMIVO doesn't really have the modeled objects,
we must get the children of the ParentObject (target) */
return super.getChildren(target);
}
@Override
public Object getParent(Object object)
{
/* Return the ParentObject */
return target;
}
@Override
public Collection getNewChildDescriptors(Object object, EditingDomain editingDomain, Object sibling)
{
/* Gets the descriptors from the ParentObject instead */
return super.getNewChildDescriptors(target, editingDomain, sibling);
}
@Override
public Object getImage(Object object)
{
/* This will be the icon for the NMIVO */
return getResourceLocator().getImage("location/of/appropriate/icon.gif");
}
@Override
public ResourceLocator getResourceLocator()
{
/* AppEditPlugin corresponds to the EMF app-specific EditPlugin class */
return AppEditPlugin.INSTANCE;
}
}
In order to allow the NMIVOs to add/remove new objects, add the following methods:
@Override
public Command createCommand(final Object object, final EditingDomain domain,
Class<? extends Command> commandClass, CommandParameter commandParameter)
{
/* The ParentObject will do the actual creation of modeled objects */
commandParameter.setOwner(target);
return super.createCommand(target, domain, commandClass, commandParameter);
}
@Override
protected Command createRemoveCommand(EditingDomain domain, EObject owner,
EStructuralFeature feature, Collection collection)
{
return createWrappedCommand(super.createRemoveCommand(domain,owner,feature,collection));
}
@Override
protected Command createAddCommand(EditingDomain domain, EObject owner,
EStructuralFeature feature, Collection collection, int index)
{
return createWrappedCommand(super.createAddCommand(domain,owner,feature,collection,index));
}
@Override
protected Command createWrappedCommand(Command command, final EObject owner)
{
/* This wrapper is used to ensure that only objects with features
supported by this NMIVO will appear under it in the Editor view */
return new CommandWrapper(command)
{
public Collection getAffectedObjects()
{
Collection affected = super.getAffectedObjects();
if (affected.contains(owner)) {
affected = Collections.singleton(TransientItemProvider.this);
}
return affected;
}
};
}
NMIVOItemProviderThe following class methods will be overriden from ItemProviderAdapter:
getChildrenFeatures()getText()collectNewChildDescriptors()createNewDragAndDropCommand()NMIVOItemProvider implementationpublic class NMIVOItemProvider
extends TransientItemProvider {
public NMIVOItemProvider(AdapterFactory adapterFactory, ParentObject parentObject)
{
super(adapterFactory, parentObject);
}
@Override
public Collection getChildrenFeatures(Object object)
{
/* Here we must make sure that the NMIVO only accepts
* the appropriate child of the containment reference
*/
if (childrenFeatures == null) {
super.getChildrenFeatures(object);
/* AppPackage corresponds to name of your EMF package (EPO1Package in the book);
* PARENTOBJECT_NMIVO corresponds to the name of the NMIVO
* (SUPPLIER_ORDERS,SUPPLIER_CUSTOMERS in the book)
*/
childrenFeatures.add(AppPackage.Literals.PARENTOBJECT__NMIVO);
}
return childrenFeatures;
}
public String getText(Object object)
{
/* Return the actual name of the NMIVO instead */
return "NMIVO";
}
protected void collectNewChildDescriptors(Collection newChildDescriptors, Object object)
{
/* This enusures that the NMIVO only knows about the associated subset of the modeled objects
* AppFactory corresponds to EPO1Factory in the book
* ``createChildObject()'' will be the method that creates the child object instead
*/
super.collectNewChildDescriptors(newChildDescriptors, object);
newChildDescriptors.add(createChildParameter(AppPackage.Literals.PARENTOBJECT_NMIVO,AppFactory.eINSTANCE.createChildObject()));
}
}
The NMIVOs must only include the objects it is associated with. In order to prevent the user from dragging an unassociated object and dropping it into the NMIVO, override the following:
/**
* Make sure that we can only drag into this tab objects that match its feature type.
*/
@Override
protected Command createDragAndDropCommand(EditingDomain domain, Object owner, float location,
int operations, int operation, Collection collection)
{
if (new AddCommand(domain, (EObject)owner, AppPackage.Literals.PARENTOBJECT__NMIVO, collection).canExecute()) {
return super.createDragAndDropCommand(domain, owner, location, operations, operation, collection);
}
return UnexecutableCommand.INSTANCE;
}
}