Menu

EMFNotificationFramework

Tom Swain
Attachments
emf-notification.jpg (37224 bytes)

EMF Notification Framework


The EMF notification framework provides the means to update EMF objects affected by user actions.
[TOC]

Notification Model Example

Notification Model Events

In BasicNotifierImpl (EMF model object implementation)

  1. A command is called via an EMF model object.
  2. The model object (as BasicNotifierImpl) calls eNotify().

    public void eNotify(Notification notification)
      {
        Adapter[] eAdapters = eBasicAdapterArray();
        if (eAdapters != null && eDeliver())
        {
          for (int i = 0, size = eAdapters.length; i < size; ++i)
          {
            eAdapters[i].notifyChanged(notification);
          }
        }
      }
    
  3. Each of the Adapters in eAdapters calls notifyChanged()

  4. The Adapters will be the item provider attached to the EMF object and the item providers of its immediate children. For instance, the adapters of EnumerationImpl will be EnumerationItemProvider and all of its NMIVOs.

In EnumerationItemProviderAdapter

        /**

     * This handles model notifications by calling {@link #updateChildren} to update any cached
     * children and by creating a viewer notification, which it passes to {@link #fireNotifyChanged}.
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated
     */
    @Override
    public void notifyChanged(Notification notification) {
        updateChildren(notification);
        switch (notification.getFeatureID(Enumeration.class)) {
            case RealPackage.ENUMERATION__STIMULI:
            case RealPackage.ENUMERATION__REQUIREMENTS:
            case RealPackage.ENUMERATION__PREDICATES:
            case RealPackage.ENUMERATION__OUTPUTS:
            case RealPackage.ENUMERATION__NAMED_RESPONSES:
            case RealPackage.ENUMERATION__INTERFACES:
            case RealPackage.ENUMERATION__MAPPINGS:
            case RealPackage.ENUMERATION__STATE_VARIABLES:
            case RealPackage.ENUMERATION__STATES:
                fireNotifyChanged(new ViewerNotification(notification, notification.getNotifier(), true, false));
                return;
        }
        super.notifyChanged(notification);
    }
  1. updateChildren() will update the children of the Notification object (i.e. ENotificationImpl) if it has a children store attached.
  2. The notification is handled based on the feature it is modifying.
  3. The item provider then calls fireNotifyChanged() (as an ItemProviderAdapter). Within that function call, the ItemProviderAdapterFactory attached to the item provider fires its fireNotifyChanged() method (as an IChangeNotifier), which in turn calls fireNotifyChanged() on all of its registered listeners (e.g. viewer objects such as TreeViewer). (see pg 52 in EMF book)

      /**
    
       * This convenience method converts the arguments into an appropriate update call on the viewer.
       * The event type is a value from the static constants in {@link org.eclipse.emf.common.notify.Notifier}.
       */
    public void fireNotifyChanged(Notification notification)
      {
    /*
        System.out.println("ItemProviderAdapterFactory.fireNotifyChanged");
        System.out.println("  object    = " + object);
        System.out.println("  eventType = " + eventType);
        if (oldValue instanceof Collection)
        {
          System.out.println("  oldValue  = " + CommandParameter.collectionToString((Collection)oldValue));
        }
        else
        {
          System.out.println("  oldValue  = " + oldValue);
        }
        if (newValue instanceof Collection)
        {
          System.out.println("  newValue  = " + CommandParameter.collectionToString((Collection)newValue));
        }
        else
        {
          System.out.println("  newValue  = " + newValue);
        }
    */
        if (changeNotifier != null)
        {
          changeNotifier.fireNotifyChanged(notification);
        }
    
        if (adapterFactory instanceof IChangeNotifier)
        {
          IChangeNotifier changeNotifier = (IChangeNotifier)adapterFactory;
          changeNotifier.fireNotifyChanged(notification);
        }
      }
    
  4. Note that super.notifyChanged(notification) is never called.

ItemProviderAdapter calls updateChildren()

 /**

   * Updates any cached children based on the given notification. If a {@link ChildrenStore} exists for its notifier,
   * then the children of the specified feature are updated.
   *
   * <p>Existing children in the store that correspond to any set, removed or unset values are {@link
   * #disposeWrapper disposed} before being removed from the store. When children are added to, removed from, or moved
   * within a feature, the indices of any others affected are {@link #adjustWrapperIndex adjusted}. Since this method
   * is typically called from {@link #notifyChanged(Notification) notifyChanged}, which, in subclasses, is often invoked repeatedly
   * up the inheritance chain, it can be safely called repeatedly for a single notification, and only the first such
   * call will have an effect. Such repeated calls may not, however, safely be interleaved with calls for another
   * notification.
   */
  protected void updateChildren(Notification notification)
  {
    EObject object = (EObject)notification.getNotifier();
    ChildrenStore childrenStore = getChildrenStore(object);

    if (childrenStore != null)
    {
      EStructuralFeature feature = (EStructuralFeature)notification.getFeature();
      EList<Object> children = childrenStore.getList(feature);
      if (children != null)
      {
        int index = notification.getPosition();

        switch (notification.getEventType())
        {
          case Notification.UNSET:
          {
            // Ignore the unset notification for an isMany feature; the value is boolean in this case.
            //
            if (feature.isMany())
            {
              break;
            }

            // continue to next case
          }
          case Notification.SET:
          {
            Object oldChild = childrenStore.get(feature, index);
            Object newValue = notification.getNewValue();

            if (unwrap(oldChild) != newValue)
            {
              if (feature.isMany() && index == Notification.NO_INDEX)
              {
                disposeWrappers((List<?>)oldChild);
              }
              else
              {
                disposeWrapper(oldChild);
              }
              Object newChild = newValue == null && index == Notification.NO_INDEX ?
                  null : wrap(object, feature, newValue, index);
              childrenStore.set(feature, index, newChild);
            }
            break;
          }
          case Notification.ADD:
          {
            EList<?> values = (EList<?>)object.eGet(feature);

            if (children.size() != values.size())
            {
              Object newValue = notification.getNewValue();
              adjustWrapperIndices(children, index, 1);
              children.add(index, wrap(object, feature, newValue, index));
            }
            break;
          }
          case Notification.REMOVE:
          {
            EList<?> values = (EList<?>)object.eGet(feature);

            if (children.size() != values.size())
            {
              disposeWrapper(children.remove(index));
              adjustWrapperIndices(children, index, -1);
            }
            break;
          }
          case Notification.ADD_MANY:
          {
            EList<?> values = (EList<?>)object.eGet(feature);

            if (children.size() != values.size())
            {
              if (notification.getOldValue() != null)
              {
                throw new IllegalArgumentException("No old value expected");
              }
              List<?> newValues = (List<?>)notification.getNewValue();
              List<Object> newChildren = new ArrayList<Object>(newValues.size());
              int offset = 0;
              for (Object newValue : newValues)
              {
                newChildren.add(wrap(object, feature, newValue, index + offset++));
              }
              adjustWrapperIndices(children, index, offset);
              children.addAll(index, newChildren);
            }
            break;
          }
          case Notification.REMOVE_MANY:
          {
            // No index specified when removing all elements.
            //
            if (index == Notification.NO_INDEX) index = 0;
            EList<?> values = (EList<?>)object.eGet(feature);

            if (children.size() != values.size())
            {
              if (notification.getNewValue() instanceof int[])
              {
                int[] indices = (int[])notification.getNewValue();
                for (int i = indices.length - 1; i >= 0; i--)
                {
                  disposeWrapper(children.remove(indices[i]));
                  adjustWrapperIndices(children, indices[i], -1);
                }
              }
              else
              {
                int len = ((List<?>)notification.getOldValue()).size();
                List<?> sl = children.subList(index, index + len);
                disposeWrappers(sl);
                sl.clear();
                adjustWrapperIndices(children, index, -len);
              }
            }
            break;
          }
          case Notification.MOVE:
          {
            int oldIndex = (Integer)notification.getOldValue();
            EList<?> values = (EList<?>)object.eGet(feature);
            boolean didMove = true;

            for (int i = Math.min(oldIndex, index), end = Math.max(oldIndex, index); didMove && i <= end; i++)
            {
              didMove = unwrap(children.get(i)) == values.get(i);
            }

            if (!didMove)
            {
              int delta = index - oldIndex;
              if (delta < 0)
              {
                adjustWrapperIndices(children, index, oldIndex, 1);
              }
              children.move(index, oldIndex);
              adjustWrapperIndex(children.get(index), delta);
              if (delta > 0)
              {
                adjustWrapperIndices(children, oldIndex, index, -1);
              }
            }
            break;
          }
        }
      }
    }
  }

Related

Wiki: Home

MongoDB Logo MongoDB