Previous Page
Next Page

20.3. Adapters

Eclipse provides an adapter framework for translating one type of object into a corresponding object of another type. This allows for new types of objects to be systematically translated into existing types of objects already known to Eclipse.

When a user selects elements in one view or editor, other views can request adapted objects from those selected objects that implement the org.eclipse.core.runtime.IAdaptable interface. This means that items selected in the Favorites view can be translated into resources and Java elements as requested by existing Eclipse views without any code modifications to them (see Section 7.4, Linking the View, on page 305).

20.3.1. IAdaptable

For objects to participate in the adapter framework, they must first implement the IAdaptable interface, as was done with IFavoriteItem (see Section 7.4.2, Adaptable objects, on page 306). The IAdaptable interface contains this single method for translating one type of object into another:

getAdapter(Class)Returns an object that is an instance of the given class and is associated with this object. Returns null if no such object can be provided.

Implementers of the IAdaptable interface attempt to provide an object of the specified type. If they cannot translate themselves, then they call the adapter manager to see whether a factory exists for translating them into the specified type.

private IResource resource;

public Object getAdapter(Class adapter) {
   if (adapter.isInstance(resource)) {
      return resource;
   }
   return Platform.getAdapterManager()
      .getAdapter(this, adapter);

}

20.3.2. Using adapters

Code that needs to translate an object passes the desired type, such as IResource.class, into the getAdapter() method, and either obtains an instance of IResource corresponding to the original object or null indicating that such a translation is not possible.

if (!(object instanceof IAdaptable)) {
   return;
}
MyInterface myObject
   = ((IAdaptable) object).getAdapter(MyInterface.class);
if (myObject == null) {
   return;
}
... do stuff with myObject ...

20.3.3. Adapter factory

Implementing the IAdaptable interface allows new types of objects, such as the Favorites items to be translated into existing types such as IResource, but how are existing types translated into new types? To accomplish this, implement the org.eclipse.core.runtime.IAdapterFactory interface to translate existing types into new types.

For example, in the Favorites product, you cannot modify the implementers of IResource, but can implement an adapter factory to translate IResource into IFavoriteItem. The getAdapterList() method returns an array indicating the types to which this factory can translate, while the getAdapter() method performs the translation. In this case, the factory can translate IResource and IJavaElement objects into IFavoriteItem objects so the getAdapterList() method returns an array containing the IFavoriteItem.class.

package com.qualityeclipse.favorites.model;

import org.eclipse.core.runtime.*;

public class FavoriteAdapterFactory
   implements IAdapterFactory
{
   private static Class[] SUPPORTED_TYPES =
      new Class[] { IFavoriteItem.class };

   public Class[] getAdapterList() {
      return SUPPORTED_TYPES;
   }

   public Object getAdapter(Object object, Class key) {
      if (IFavoriteItem.class.equals(key)) {
         FavoritesManager mgr = FavoritesManager.getManager();
         IFavoriteItem item = mgr.existingFavoriteFor(object);
         if (item == null) {
            item = mgr.newFavoriteFor(object);
         }
         return item;
      }
      return null;
    }
}

Adapter factories must be registered with the adapter manager before they are used. Typically, a plug-in registers adapters with adapter managers when it starts up and unregisters them when it shuts down. For example, in the Favorites product, add the following field to the FavoritesPlugin class:

private FavoriteAdapterFactory favoriteAdapterFactory;

The following code, added to the FavoritesPlugin's start() method, registers the adapter. The FavoriteAdapterFactory translates IResource and IJavaElement objects into IFavoriteItem objects, so you can register the adapter once with IResource.class as the argument and a second time with IJavaElement.class indicating that the adapter factory can translate from these types to others.

favoriteAdapterFactory = new FavoriteAdapterFactory();
IAdapterManager mgr = Platform.getAdapterManager();
mgr.registerAdapters(favoriteAdapterFactory, IResource.class);
mgr.registerAdapters(favoriteAdapterFactory, IJavaElement.class);

In addition, the FavoritesPlugin's stop() method must be modified to unregister the adapter:

Platform.getAdapterManager().unregisterAdapters(
   favoriteAdapterFactory);
favoriteAdapterFactory = null;

The introduction of an adapter factory allows code in the Favorites product and any plug-ins that depend on it to be more loosely coupled with the FavoritesManager. For example, rather than directly calling the FavoritesManager class, the FavoritesView.pageSelectionChanged() method (see Section 7.4.3, Selection listener, on page 306) can be revised to use the adaptable interface.

protected void pageSelectionChanged(
   IWorkbenchPart part, ISelection selection)
{
   if (part == this) {
      return;
   }
   if (!(selection instanceof IStructuredSelection)) {
      return;
   }

   IStructuredSelection sel = (IStructuredSelection) selection;

   List items = new ArrayList();
   Iterator iter = sel.iterator();
   while (iter.hasNext()) {
      Object object = iter.next();
      if (!(object instanceof IAdaptable)) {
         continue;
      }

      IFavoriteItem item = (IFavoriteItem)
         ((IAdaptable) object).getAdapter(IFavoriteItem.class);

      if (item == null) {
         continue;
      }
      items.add(item);
   }

   if (items.size() > 0) {
      viewer.setSelection(new StructuredSelection(items), true);
   }
}

Using an adapter factory has a bit more overhead than referencing the FavoritesManager directly. When considering this for your own product, you will need to determine whether the advantage of looser coupling out-weighs the additional complexity and slightly slower execution time inherent in this approach.

20.3.4. IWorkbenchAdapter

Eclipse uses the IWorkbenchAdapter interface to display information. Many Eclipse views, such as the Navigator view, create an instance of WorkbenchLabelProvider. This label provider uses the IAdaptable interface to translate unknown objects into instances of IWorkbenchAdapter, and then queries this translated object for displayable information such as text and images.

For your object to be displayed in an Eclipse view, such as the Navigator, implement the IAdaptable interface and return an object that implements IWorkbenchAdapter or extends the WorkbenchAdapter abstract base class.


Previous Page
Next Page