Dr. Stefan Winkler
freier Softwareentwickler und IT-Berater

One of the first Hello World examples when dealing with contributions to Eclipse extension points is adding an objectContribution or a viewerContribution to a popup menu. However, if one wants to add a more flexible contribution which is hided or disabled in certain circumstances, documentation gets rare. This article describes how to implement these advanced featues by example. Let's assume we have an EMF model of objects MyObject which implement a property called type. EMF can generate item and label providers for tree editors. Let's assume we have implemented such an editor and we want to contribute an action which is only visible for a particular type value.

Update: This article uses the old API of contributing actions to views' and objects' popup menus. There is a new API which is described in a more recent post.

As a first step we implement the corresponding ActionDelegate and register it as an object contribution: This

<extension point="org.eclipse.ui.popupMenus">
   <objectContribution
            adaptable="false"
            id="my.contribution.id"
            objectClass="my.model.MyObject">
         <action
               class="my.contribution.MyActionDelegate"
               enablesFor="1"
               id="my.contribution.action.id"
               label="Do something only with type FOO"
               menubarPath="additions">
         </action>
  </objectContribution>
</extension>

This way, however, the contribution is visible and enabled always. We want to hide (or disable) it, if selectedObject.getType() is not equal to "FOO". The popupMenus extension point lets us specify visibility and enablement constraints and we can use the objectState element to query the object's state. However, this is not as simple as it sounds, because objectState can only query objects which implement the IActionFilter interface and since MyObject is a EMF generated class, we cannot simply implement the interface. But what we can do, is adapt. So we write an adapter and its factory:

public class MyObjectToActionFilterAdapter implements IActionFilter {

    private static final Object MYOBJECT_TYPE = "objectType";

    private static ArtifactToActionFilterAdapter INSTANCE = new ArtifactToActionFilterAdapter();

    private ArtifactToActionFilterAdapter() {}

    @Override
    public boolean testAttribute(Object target, String name, String value) {
        if (target instanceof MyObject) {
            MyObject obj = (MyObject) target;

            if(MYOBJECT_TYPE.equals(name)) {
                return value.equals(obj.getType());
            }
        }

        return false;
    }

    public static ArtifactToActionFilterAdapter getInstance() {
        return INSTANCE;
    }
}

public class MyActionFilterAdapterFactory implements IAdapterFactory {

    @SuppressWarnings("unchecked")
    @Override
    public Object getAdapter(Object adaptableObject, Class adapterType) {
        if(adapterType == IActionFilter.class)
            return MyObjectToActionFilterAdapter.getInstance();
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Class[] getAdapterList() {
        return new Class[] {IActionFilter.class};
    }
}

Note that the adapter can be implemented as a singleton because the testAttribute implementation is called with the object to be tested. Now we have to register our adapter factory using:

<extension point="org.eclipse.core.runtime.adapters">
      <factory
            adaptableType="my.model.MyObject"
            class="my.contribution.MyActionFilterAdapterFactory">
         <adapter
               type="org.eclipse.ui.IActionFilter">
         </adapter>
      </factory>
</extension>

and after that we are ready to declare our constraints:

<extension point="org.eclipse.ui.popupMenus">
   <objectContribution
            adaptable="false"
            id="my.contribution.id"
            objectClass="my.model.MyObject">
         <action
               class="my.contribution.MyActionDelegate"
               enablesFor="1"
               id="my.contribution.action.id"
               label="Do something only with type FOO"
               menubarPath="additions">
         </action>
         <visibility>
               <objectState
                     name="objectType"
                     value="FOO">
               </objectState>
         </visibility>
  </objectContribution>
</extension>

This causes the action to appear only if the property type of MyObject has the value FOO. Instead of hiding the action, we can also disable it using the element <enablement> istead of <visibility>. In this case, however, the <enablement> constraint has to be a child of the <action> element instead of a sibling.