Menu

NonModeledIntermediaryViewObjects

Tom Swain

Non-Modeled Intermediary View Objects


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]

Naming Conventions


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)

EMF Generator Model changes


Before adding the NMIVOs, make the following changes to the EMF Generator Model (.genmodel) and regenerate the Edit code:

  1. Double-click on the .genmodel file
  2. In the Properties View of ParentObject, change Edit:Provider Type to Stateful
  3. Expand the contents of the ParentObject, and for each ChildObject change Edit:Create Child to False. This prevents the editor from allowing the user to create children from the ParentObject.

Changes to ParentItemProvider


Class variables

The following class variables will be created:

  • children (type List)

Class methods

The following methods will be added:

  • getFirstNMIVOItemProvider()
  • getSecondNMIVOItemProvider()
    ...and so forth...
    The following methods will be overriden from ItemProviderAdapter:
  • getChildren()
  • createRemoveCommand()
  • createAddCommand()
  • createWrappedCommand()
  • dispose()

Adding NMIVOs to the ParentItemProvider

protected 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;
}

Adding accessors for the NMIVOs

  /* 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;
  }

  ...

Overriding 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;
  }

Disposal of non-modeled objects

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();
      }
    }
  }


Changes to ChildItemProvider


Class methods

The following methods will be overriden:

  • getParent()

NMIVOs as parents of modeled child objects

  /**

   * 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;
  }

Creating the class TransientItemProvider


Class methods

The following methods will be overriden from ItemProviderAdapter:

  • TransientSupplierItemProvider() (constructor)
  • getChildren()
  • getParent()
  • getNewChildDescriptors()
  • getImage()
  • getResourceLocator()
  • createCommand()
  • createAddCommand()
  • createWrappedCommand()

TransientItemProvider implementation

public 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;
  }
}

Adding commands

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;
      }
    };
  }

Creating the class NMIVOItemProvider


Class methods

The following class methods will be overriden from ItemProviderAdapter:

  • getChildrenFeatures()
  • getText()
  • collectNewChildDescriptors()
  • createNewDragAndDropCommand()

NMIVOItemProvider implementation

public 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()));
  }

}

Preventing associations with unwanted model objects

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;
    }
  }

Related

Wiki: EMFEditOverview
Wiki: Home

MongoDB Logo MongoDB