Support for adaptable contributions in Eclipse 3.1

Last Updated: February 10th, 2005
by Jean-Michel Lemieux

Context

All adaptable workbench contributions only support adapting to IResource.

Problem

An adaptable flag was added to three workbench extension points to allow contributions (e.g. popupMenus, property pages, decorators) to be applied to classes that do not share a common ancestor. For example, to enable CVS contributions to appear in the Java Packages View. The CVS plug-in defines adaptable contributions on IResource types and JDT would provide adapters from Java elements to IResources. The problem is that adaptable contributions are currently limited to IResource classes.

Adapting models: the key to rich integration

In Eclipse plug-ins are free to model their application data using their model of choice. A key to providing integration in Eclipse is to allow bridging of the models. A model is a fairly generic term, but is known to most people as the actual data representation for an application. But there are actually three types of models:

Types Descirption
Meta-model A model that includes types whose instances are also types. Meta-models are often used to specify other models. For example, EMF provides a meta-model (ECore) for describing models.
Model A model of a specific application domain. These instances are called meta-objects. A model is the actual data representation for an application. For example, in JDT the model is composed of ICompilationUnit, IJavaProject... The model can be instances of a meta-model or a standalone model (e.g. simply a set of classes and interfaces).
User Objects
Instances of the elements of a model.

Figure 1 describes the common relationships between plug-ins and models in Eclipse. In this example, Model A is defined as a base model; an adapter is defined for elements from model B and C that will return and element from model A. Adaptable contributions is the mechanism that allows models to accept contributions made to base models without requiring the model implementation to inherit or implement classes from the base model.


Figure 1: Plug-ins and models

Currently the only base model supported in Eclipse is IResource. The goal for opening this up is to allow other base models to exist and thus allow bridging between other domain models.

Note: When adapting to a base model it is not fair to accept that all adapters represent a 1-1 mapping between model elements. Consider providing a mapping interface which allows a flexible representation of the mapping between two models.

Adapters

A historic problem with Adapters in Eclipse was that they were defined at runtime. This meant that using them for contributions was not possible since plug-in that were'nt loaded would not get a chance to contribute adapters. The result was that contributions may not be visible until a plug-in was loaded. This was solved in 3.0 with the support for declarative specification of adapter factories.

Contributions are adapted using the adapter factory to query the supported adapters for a given type. This means that for adaptable contributions to be supported plug-ins must move any runtime adapter specification to use the new declarative extension point. There are two distinct participants involved in supporting adaptable contributions:
  1. A plug-in that defines a model must define the adapters it supports.
  2. A plug-in that defines common contributions must define the base model elements they apply to and ensure that the contributions are marked adaptable.
Defining the adapters

A plug-in can define the adapters it supports by registering an IAdapterFactory with the org.eclipse.runtime.adapters extension point as follows:
<extension
point="org.eclipse.core.runtime.adapters">
<factory
class="logicalView.views.AdapterFactory"
adaptableType="logicalView.views.MyLogicalElement">
<adapter type="org.eclipse.core.resources.mapping.ResourceMapping"/>
</factory>
</extension>
This plugin.xml snippet defines an adapter that can adapt instances of MyLogicalElement to ResourceMapping. An adapter factory can adapt the same adaptableType to different types. Simply add another adapter element to the extension point. The adapter factory implementation would be:
public class AdapterFactory implements IAdapterFactory {
public Object getAdapter(Object adaptable, Class adapterType) {
if(adaptableinstanceof MyLogicalElement &&
adapterType == ResourceMapping.class) {
return new MyLogicalElementResourceMapping(
(MyLogicalElement)adaptable);
}
return null;
}

public Class[] getAdapterList() {
return new Class[] {IResourceMapper.class};
}
}
A rule of thumb when deciding what adapters to register, try and adapt to the most specific type possible. For example, although it is technically possible to return the IProject for a java IMethod (e.g. the project that contains the method) the relationship is too vague and would be best to return the IFile instead.

Definining the contributions


The syntax for defining contributions hasn't changed simply, that the adaptable attribute will be honoured even when the object class is not IResource. Here is a snippet for defining a popup menu that should appear on ResourceMapping instances:
<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
objectClass="org.eclipse.core.resources.mapping.ResourceMapping"
adaptable="true"
id="myaction.id">
<action
label="Do It"
class="myplug.actions.DoItAction"
menubarPath="additions"
id="myaction.id.doit">
</action>
</objectContribution></extension>
The implementation of the action can assume the following:

IContributorResourceAdapter

An interface was added in Eclipse 2.0 to allow plug-ins to adapt IResource elements differently when they were used for workbench contributions (see Bug 6247 for a background discussion). This interface is still honnoured when adapting to IResource by first checking if an objects adapts to IContributorResourceAdapter then to IResource. In 3.1, a subinterface IContributorResourceAdapter2 has been added to support context dependant adaptation to ResourceMappings.

Note: This class is defined in the IDE plug-in meaning that applications not including the IDE can't use this support.

Problems with IActionFilter

Action filters are designed to allow contributions to filter their visibility based on a filter queried at runtime. The workbench tries to find an IActionFilter for an object in the selection by asking the adapted selection if it can adapt to IActionFilter. This is restrictive since it allows only one implementation of an IActionFilter per type. Another more flexible approach is to use the new enablement attribute that was added in 3.0. The following enablement will show the contribution only if it is shared with a CVS project. The enablement tester is provived in the contribution instead of via the selection, which makes this approach more flexible.

<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
objectClass="org.eclipse.core.resources.mapping.ResourceMapping"
adaptable="true"
id="myaction.id">
<enablement>
<test property="org.eclipse.ui.ide.projectPersistentProperty" args="org.eclipse.team.core.repository=org.eclipse.team.cvs.core.cvsnature" />
</enablement>
...
</objectContribution></extension>
The property tester would is contributed as follows:
<extension
point="org.eclipse.core.expressions.propertyTesters">
<propertyTester namespace="org.eclipse.ui.ide" type="org.eclipse.core.resources.mapping.ResourceMapping" class="org.eclipse.ui.internal.ide.model.ProjectPersistentPropertyTester" properties="projectPersistentProperty" id="org.eclipse.ui.ide.projectPersistentPropertyTester" />
</extension>
Related Bugs

https://bugs.eclipse.org/bugs/show_bug.cgi?id=32436
https://bugs.eclipse.org/bugs/show_bug.cgi?id=53217
https://bugs.eclipse.org/bugs/show_bug.cgi?id=76599
https://bugs.eclipse.org/bugs/show_bug.cgi?id=38041
https://bugs.eclipse.org/bugs/show_bug.cgi?id=36968
https://bugs.eclipse.org/bugs/show_bug.cgi?id=37084

Related problems not addressed