EMF.Edit Performance

Whenever you are using EMF generated models in rich client applications don’t forget to generate appropriate EMF.Edit item providers for all your domain objects. Even if you are using other UI frameworks than SWT, this will definitely save a lot of time. Especially if your user interface has to react on complex model changes. Complex changes are bulked collection modifications that might trigger refresh operations in table or tree based viewers.

If you are not familiar with the basic concepts of EMF and EMF.Edit the What every Eclipse developer should know about EMF blog post is a good starting point.

As you can see in the illustration below, the AdapterFactoryContentProvider reacts on model changes (actually notified by the wrapped AdapterFactory and a specific ModelItemProvider) and refreshes its associated viewer instance.

 

This works pretty fine in most cases. However if your underlying domain model is very ‘nervous’ with dozens of model updates per second (e.g. stock prices, simulation models, sensor data) frequent refresh operations slow down the user interface. In this case you should try to use a delayed content provider illustrated here:

 

 

Instead of performing dozens of smaller viewer updates it could be better to await a couple of model notifications and perform a full refresh operations afterwards.

The snippet shows an exemplary implementation that I currently use in a customers monitoring application for high frequent simulation models. The provider implementation itself is time based. If the first notification arrives, a full viewer refresh is performed after a short delay (Display.timerExec(..)). Notifications arriving in the meantime can be ignored due to the subsequent full viewer refresh. Delays around 500ms feel a little lazy but the UI reacts still as smooth as expected.

 


import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Display;

/**
 * * Use this {@link AdapterFactoryContentProvider} if the viewer's underlying
 * domain model frequently changes and would produce many notification events.
 * In this case it's much more efficient to trigger a full refresh after a short
 * delay.
 */
public class DelayedAdapterFactoryContentProvider extends
		AdapterFactoryContentProvider {

	private static final int DELAY_IN_MS = 500;
	private final AtomicBoolean refresh = new AtomicBoolean(false);
	private Viewer viewer;

	public DelayedAdapterFactoryContentProvider(AdapterFactory adapterFactory) {
		super(adapterFactory);
	}

	@Override
	public void notifyChanged(Notification notification) {

		if (!notification.isTouch() && !refresh.getAndSet(true)) {

			Display.getDefault().asyncExec(new Runnable() {
				@Override
				public void run() {

					Display.getDefault().timerExec(DELAY_IN_MS, new Runnable() {
						@Override
						public void run() {
							refresh.set(false);

							if (viewer.getControl() != null
									&& !viewer.getControl().isDisposed()) {
								viewer.refresh();
							}
						}
					});
				}
			});

		}
	}

	@Override
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		this.viewer = viewer;
		super.inputChanged(viewer, oldInput, newInput);
	}
}