• Share this article:

Adapting

Monday, November 5, 2007 - 10:48 by Wayne Beaton

Over the past week or so, I’ve been discussing how you can make your objects adaptable into different forms. This is great for getting your objects to tightly integrate with existing parts of the Eclipse infrastructure, while remaining loosely coupled with the actual implementation. Loose coupling with tight integration is pretty powerful stuff.

A while back, I created an “Image Preview” view that displays the image (if one is available) for a file selected in the workbench (it works best for image files). The best part is that the view is completely decoupled from the Resources API. That is, it doesn’t know anything about files, directories, workspaces, or anything along those lines. By implementing the image viewer using adapters, I can “teach” my view to display an image for different kinds of selected objects by providing a new adapter. A natural extension of this is that I can use my Image Preview view in an RCP application to display an image for my domain objects (assuming that this makes sense, of course).

The Image Preview view listens to the workbench selection service. When a selection occurs in a view (such as the Package Explorer, or Navigator), the selection service notifies registered listeners (see here for more information on the selection service). When the Image Preview view is notified of the selection change, it attempts to adapt the selected object to the ImageProvider interface (which is part of my implementation). The ImageProvider is then used to obtain an image. The getImageProvider method in the Image Preview view looks like this:

private ImageProvider getImageProvider(Object object) {
	// First, if the object is an ImageProvider, use it.
	if (ImageProvider.class.isInstance(object)) return (ImageProvider)object;

	// Second, if the object is adaptable, ask it to get an adapter.
	ImageProvider provider = null;
	if (object instanceof IAdaptable)
		provider = (ImageProvider)((IAdaptable)object).getAdapter(ImageProvider.class);

	// If we haven't found an adapter yet, try asking the AdapterManager.
	if (provider == null)
		provider = (ImageProvider)Platform.getAdapterManager().loadAdapter(object, ImageProvider.class.getName());

	return provider;
}

The first step, is to see if the selected object already implements our interface. If it does, we cast and return it. If we make it to the second step, we ask the object if it is adaptable (i.e. does it implement the IAdaptable interface). If it does, we use that method to attempt to find an adapter. If that method fails (returns null), the AdapterManager is used. Ultimately, this method may fail to find an appropriate adapter and return null.

The selected object doesn’t need to know anything about the Image Preview view. Conversely, my Image Preview view knows nothing about files. The adapter interface knows about both (it really only knows about the ImageProvider interface).

I’m curious about the history of this little pattern (which is repeated many times). It seems that there is an opportunity here to have a higher-level API in the AdapterManager that takes all of these steps, but I assume that there is a good (or at least historical) reason for it being the way that it is.

Note that there are two different ways to ask the AdapterManager to adapt an object: getAdapter or loadAdapter (which is highlighted in the snippet). Both methods will find programmatically- and declaratively-registered adapters, however the getAdapter method will only find declaratively-registered adapters if the bundle that contributes them has been activated. The loadAdapter will load and activate the bundle (if required) as part of the process.