EMF Notification

In EMF, the generated model interface code extends from the EObject interface. This makes it possible to obtain the modeled object’s Metadata, the Container, and the Resource in which it is persisted. In addition, you are provided with an API used for accessing the object reflectively.

The interface EObject extends from the Notifier interface. An EObject instance sends a notification whenever the value of one of its structural feature changes. In the below code, a notification is sent whenever the value of the attribute code is changed. The call to eNotificationRequired() ensures that there are model change listeners before calling the eNotify() method.

public void setCode(String newCode) {
	String oldCode = code;
	code = newCode;
	if (eNotificationRequired())
		eNotify(new ENotificationImpl(this, Notification.SET,
		CreditcardPackage.REGION__CODE, oldCode, code));
}

EMF Adapters

The Adapters in EMF are the model change listeners (A.K.A Observers). In addition, they can be used for extending the behavior of an EOject/Resource/ResourceSet without subclassing. The below code demonstrates its use

Listening to model changes

public class RegionChangeListener extends AdapterImpl {
	@Override
	public void notifyChanged(Notification notification) {
		super.notifyChanged(notification);

		final String eClassName = ((Region) notification.getNotifier()).eClass().getName();
		final String featureName = ((EAttribute)notification.getFeature()).getName();
		System.out.println(String.format("Feature `%1$s` of EClass `%2$s` Changed", featureName, eClassName));
	}
}

The RegionChangeListener listens to the changes made to the Region model instance. The overridden method notifyChanged() is invoked whenever the state of the Region changes, for example, the value of the attribute code is updated. The parameter Notification can be used for obtaining the information of the source EObject (A.K.A Notifier), the affected structural feature, the type of the change, and the new and the old values. The adapter can be hooked onto the Region instance using region.eAdapters().add(new RegionChangeObserver(), and removed using region.eAdapters().remove(regionChangeAdaterInstance). Removing the adapter from an EObject will remove it from all its contents.

Extending the EObject/Resource/ResourceSet behavior

public class ProjectAdapter extends AdapterImpl {

	Private final IProject project;

	public ProjectAdapter(IProject project) {
		This.project = project
	}
	public IProject getProject() {
		return project;
	}
	public boolean isAdapterOfType(Object object) {
		return object == IProject.class;
	}
}
resourceSet.eAdapters().add(new ProjectAdapter())

In the above example, we extend the behavior of the EMF ResourceSet by associating a ProjectAdapter. The extended ResourceSet can now give the information about the Project that was used for its creation. The associated ProjectAdapter and hence the Project can be obtained using EcoreUtil.getAdapter(resourceSet.eAdapters(), IProject.class), and projectAdapter.getProject().

EMF Content Adapter

The above-explained approach of explicitly attaching an adapter is convenient while working with a handful of EObject’s. It easily becomes impractical when you have to deal with a deep object hierarchy. Iterating through the containment hierarchy and explicitly attaching the adapter makes sense only if you intend to do it for selected objects. You also have to manage the Adapter’s lifecycle - attaching and disposing of the adapters as the object hierarchy changes.

EMF provides EContentAdapter for situations where you want to be notified for changes to any object in the hierarchy. All you have to do is to attach the adapter to the root-EObject/Resource/ ResourceSet, and the adapter automatically attaches itself to all its contents. Once attached, it will receive the notification of the state changes to any of the objects in the hierarchy, and it will respond to the content change notifications itself, by attaching and detaching itself as appropriate. All this makes the EContentAdapter quite convenient.

The following code shows how an EContentAdapter can be defined and attached to an EObject

public class ProductStateChangeAdapter extends EContentAdapter {
	@Override
	public void notifyChanged(Notification notification) {
		// Your code comes here…
	}
}
product.eAdapters().add(new ProductStateChangeAdapter());

Dos and Don’ts

References

EMF: Eclipse Modeling Framework Book, EMF discussion forum, emf-dos-and-donts-7, emf-dos-and-donts-8.