Eclipse Tips & Tricks: Property Testers with Command Core Expressions

Recently I was challenged getting declared commands enabled in an RCP application using own property testers. It seems the documentation of Eclipse’s Command Core Expressions framework is missing an important link that I’m gonna point out here.

RCP Application Plug-in

For this example create a new Plug-in project called de.rowlo.rcp.cce.app. Make sure to create a rich client application and select the “RCP application with a view” template.

You should now have a new project in your workspace that’s called de.rowlo.rcp.cce.app. If you right-click it and select Run As|Eclipse Application you should see the following application window:

First export the only package that there is as you’ll need to access its classes from another plug-in later. Open the Manifest editor, select the “Runtime” tab, and add the package to the list of exported packages.

An additonal preparation is to add a public getter for the View’s treeViewer as it’s needed later.
View.java:

1
2
3
4
5
6
7
8
9
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.part.ViewPart;
 
public class View extends ViewPart {
	...
	public Viewer getViewer() {
		return viewer;
	}
}

Now lets activate the main toolbar and add two commands “Foo” and “Bar” to it.
ApplicationWorkbenchWindowAdvisor:

1
2
3
4
5
	public void preWindowOpen() {
		...
		configurer.setShowCoolBar(true);
		...
	}

plugin.xml (declare commands):

1
2
3
4
5
6
7
8
9
10
11
   <extension
         point="org.eclipse.ui.commands">
      <command
            id="de.rowlo.rcp.cce.app.command.Foo"
            name="Foo">
      </command>
      <command
            id="de.rowlo.rcp.cce.app.command.Bar"
            name="Bar">
      </command>
   </extension>

plugin.xml (add commands to toolbar using the images and ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   <extension
         point="org.eclipse.ui.menus">
      ...
      <menuContribution
            locationURI="toolbar:org.eclipse.ui.main.toolbar">
         <toolbar
               id="de.rowlo.rcp.cce.app.toolbar">
             <command
                   commandId="de.rowlo.rcp.cce.app.command.Foo"
                   icon="icons/foo.png"
                   label="Foo"
                   style="push">
             </command>
             <command
                   commandId="de.rowlo.rcp.cce.app.command.Bar"
                   icon="icons/bar.png"
                   label="Bar"
                   style="push">
             </command>
         </toolbar>
      </menuContribution>
   </extension>

If you run the application now you should see the toolbar containing the two commands “Foo” and “Bar” but both are greyed out.

One of the great things of the Command framework is that the implementation of commands is decoupled from the declaration. Handlers declared via extension points take care of the implementation. It’s even possible to declare several handlers for the same command. The Command Core Expression framework then takes care of choosing the right handler depending on the expression defined in the enabledWhen element in the handler declaration.

Some expression examples can be found on the Command Core Expressions wiki page. But once you start utilizing this powerful framework the default variables will not be sufficient. You’ll want to provide own test variables and very likely even in a different plug-in.

Handler Plug-in

So lets create a second plug-in project called de.rowlo.rcp.cce.foobar. This time make sure it is not a rich client application but a plain plug-in. No need to choose a template either.

The Manifest editor should open automatically. First you need to add a plug-in dependency to org.eclipse.core.expressions and to de.rowlo.rcp.cce.app. Save the Manifest editor to force it to evaluate the available extensions. Then switch to the “Extensions” tab.

Next thing to do is to declare a property tester using the extension point “org.eclipse.core.expressions.propertyTesters”.
plugin.xml:

1
2
3
4
5
6
7
8
9
10
   <extension
         point="org.eclipse.core.expressions.propertyTesters">
      <propertyTester
            class="de.rowlo.rcp.cce.foobar.FooBarPropertyTester"
            id="de.rowlo.rcp.cce.foobar.FooBarPropertyTester"
            namespace="de.rowlo.rcp.cce.foobar"
            properties="canFoo,canBar"
            type="java.lang.Object">
      </propertyTester>
   </extension>

The most important elements in the above declaration are “namespace” and “properties”. The properties element holds a comma separated list of the variables you wish to contribute to the Command Core Expressions framework. In this case the variables “canFoo” and “canBar” are contributed. The namespace is important, too, as you have to refer to variables by a fully qualified name that consists of two parts: the namespace and the name of the variable in that namespace.
The type is used to filter the elements the property tester can be applied to. It will not be used in this example. To “disable” the filter it’s set to java.lang.Object.

Now lets take a look at the FooBarPropertyTester class. This initial implementation will be extended later on.
FooBarPropertyTester.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.eclipse.core.expressions.PropertyTester;
 
public class FooBarPropertyTester extends PropertyTester {
	public static final String PROPERTY_NAMESPACE = "de.rowlo.rcp.cce.foobar";
	public static final String PROPERTY_CAN_FOO = "canFoo";
	public static final String PROPERTY_CAN_BAR = "canBar";
 
	@Override
	public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
		if (PROPERTY_CAN_FOO.equals(property)) {
			return true;
		} else if (PROPERTY_CAN_BAR.equals(property)) {
			return true;
		}
		return false;
	}
}

First we define some constants for the namespace and supported variable names as they might be useful later.
The test() method is called by the Command Core Expressions framework for a property name to check if a handler should be enabled. For simplicity for both properties true is returned here. You might put whatever logic into that if-blocks to match you needs.

Well, last thing to do is to declare and implement the handlers for the “Foo” and “Bar” commands.
plugin.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   <extension
         point="org.eclipse.ui.handlers">
      <handler
            class="de.rowlo.rcp.cce.foobar.handler.FooHandler"
            commandId="de.rowlo.rcp.cce.app.command.Foo">
         <enabledWhen>
            <test
                  property="de.rowlo.rcp.cce.foobar.canFoo">
            </test>
         </enabledWhen>
      </handler>
      <handler
            class="de.rowlo.rcp.cce.foobar.handler.BarHandler"
            commandId="de.rowlo.rcp.cce.app.command.Bar">
         <enabledWhen>
            <test
                  property="de.rowlo.rcp.cce.foobar.canBar">
            </test>
         </enabledWhen>
      </handler>
   </extension>

As you can see for the FooHandler using the enabledWhen element you can test for a property using its fully qualified name (namespace + “.” + property’s name).

FooHandler.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
 
public class FooHandler extends AbstractHandler {
	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {
		Display display = PlatformUI.getWorkbench().getDisplay();
		MessageDialog.openInformation(display.getActiveShell(), "Foo Command", "Foo executed.");
		return null;
	}
}

The BarHandler class looks just alike.
BarHandler.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
 
public class BarHandler extends AbstractHandler {
	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {
		Display display = PlatformUI.getWorkbench().getDisplay();
		MessageDialog.openInformation(display.getActiveShell(), "Bar Command", "Bar executed.");
		return null;
	}
}

It’s time to test the new foobar plug-in to the RCP application. Open Run|Run Configurations select the run configuration of the previously created RCP application. Once the dialog has loaded select its “Plug-ins” tab and make sure the new de.rowlo.rcp.cce.foobar plug-in is checked, too. Then apply and run the RCP application with the new plugin.

Surprise: The RCP application won’t have the “Foo” and “Bar” commands enabled in its toolbar. What happend? Let’s set a break point in the FooBarPropertyTester’s test() method to check if its even called and if it is to check the parameters.

Surprise again: The Debugger didn’t stop. What’s going on? Well, let’s put a breakpoint in the “de.rowlo.rcp.cce.foobar” plug-in’s Activator.start() method. Still, the debugger won’t interrupt execution. What happend is that Eclipse’s lazy loading mechanism didn’t activate the plug-in as none of its classes is needed by means of regular Java-dependency. Although we declared a handler in the plugin.xml doesn’t mean that the plug-in is actually needed. Even though the handler is attached to a command that is visible in the UI. It doesn’t even matter if the property tester would return true always. All these things have been declared in the plugin.xml file. As long as no Java class of that plug-in is required by any other plug-in it will not become activated. It looks like that the plugin.xml is not being evaluated at all.

So, a workaround would be to just create an instance of any class of the de.rowlo.rcp.cce.foobar plug-in in the application’s activator’s start() method and discard it instantly. The classloader would load that class which in turn would lead to plug-in activation causing the evaluation of the plugin.xml. But to prevent dependency cycles this solution is no option.

Early Startup Trigger

What is an option then? Hard to believe, but true: you need to define a start up trigger in the plug-in that doesn’t get activated. That is done via the extension point: org.eclipse.ui.startup.
plugin.xml:

1
2
3
4
5
6
   <extension
         point="org.eclipse.ui.startup">
      <startup
            class="de.rowlo.rcp.cce.foobar.ActivationTriggerDummy">
      </startup>
   </extension>

ActivationTriggerDummy.java:

1
2
3
4
5
6
7
8
import org.eclipse.ui.IStartup;
 
public class ActivationTriggerDummy implements IStartup {
	@Override
	public void earlyStartup() {
		// don't do anything at all
	}
}

It’s totally wired that the declaration of the startup trigger in a plugin.xml of a plug-in not being activated leads to the activation of that plug-in. Funny, though, that it’s working.

With the ActivationTriggerDummy “implemented” you can now verify using a breakpoint in the Activator’s start() method that the plug-in becomes activated finally. But still, the toolbar commands are not enabled, not even when the viewer selection changes. There’s still something more odd about the Command Core Expression framework.

Requesting Evaluation of Expressions

The command framework is quite lazy. It doesn’t really care about your expressions and your wishes about when handlers should be enabled or not. It’s your duty to refresh the enabled state of your handlers by telling the IEvaluationService. It’s not wise to refresh the commands regularily as it can be quite expensive. Instead triggering the refresh on events is the way to go.

E.g. in this example RCP application a selection change in the viewer might be a useful event. So lets use the Activator’s start() and stop() methods to hook a listener to the viewer.
Activator.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Activator extends AbstractUIPlugin {
	...
	private FooBarSelectionListener fooBarSelectionListener;
	...
	public void start(BundleContext context) throws Exception {
		...
		fooBarSelectionListener = new FooBarSelectionListener();
		fooBarSelectionListener.hookOnView(View.ID);
	}
	...
	public void stop(BundleContext context) throws Exception {
		if (fooBarSelectionListener != null) {
			fooBarSelectionListener.dispose();
		}
		...
	}
	...
}

The FooBarSelectionListener is the most complex class so far as it needs to take care of the listening and refreshing. So I’ll go through the class method by method.
FooBarSelectionListener.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import static de.rowlo.rcp.cce.foobar.FooBarPropertyTester.PROPERTY_CAN_BAR;
import static de.rowlo.rcp.cce.foobar.FooBarPropertyTester.PROPERTY_CAN_FOO;
import static de.rowlo.rcp.cce.foobar.FooBarPropertyTester.PROPERTY_NAMESPACE;
 
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.services.IEvaluationService;
 
import de.rowlo.rcp.cce.app.View;
 
public class FooBarSelectionListener implements ISelectionChangedListener {
	...
}

The class starts with some static imports required to assemble the property names later.

The first group of methods control the life cycle of the listener. It will add itself to the only view of this example application. But it will also remove itself upon disposal. Another thing to notice is that it uses a UIJob to call back the hookOnViewer method as long as the workbench is still starting. This is necessary, as the listener is created by the Activator and as long as the workbench is starting views and viewers might not be available.
The method Viewer findViewer() is a useful utility method for later so it’s made public static here.
FooBarSelectionListener.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
	...
	private Viewer viewer;
 
	public void dispose() {
		if (viewer != null) {
			viewer.removeSelectionChangedListener(this);
		}
	}
 
	public void hookOnViewer(final String viewerId) {
		IWorkbench workbench = PlatformUI.getWorkbench();
		if (viewerId != null &amp;&amp; workbench != null &amp;&amp; workbench.getDisplay() != null) {
			Display display = workbench.getDisplay();
			Thread displayThread = display.getThread();
			if (workbench.isStarting() || !Thread.currentThread().equals(displayThread)) {
				// while workbench is starting defer hooking until later
				UIJob job = new UIJob(display, "viewer hooker") {
					@Override
					public IStatus runInUIThread(IProgressMonitor monitor) {
						hookOnViewer(viewerId);
						return Status.OK_STATUS;
					}
				};
				job.schedule(250);
			} else if (viewerId != null) {
				Viewer viewer = findViewer(viewerId);
				if (viewer != null) {
					if (this.viewer != null) {
						this.viewer.removeSelectionChangedListener(this);
					}
					requestRefresh();
					viewer.addSelectionChangedListener(this);
					this.viewer = viewer;
				}
			}
		}
	}
 
	public static Viewer findViewer(String viewId) {
		IWorkbench workbench = PlatformUI.getWorkbench();
		if (workbench != null &amp;&amp; viewId != null) {
			IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
			if (window != null) {
				IWorkbenchPage page = window.getActivePage();
				if (page != null) {
					IViewReference viewReference = page.findViewReference(viewId);
					if (viewReference != null) {
						IViewPart view = viewReference.getView(false);
						if (view instanceof View) {
							return ((View) view).getViewer();
						}
					}
				}
			}
		}
		return null;
	}
	...

The last group of methods deals with refreshing the commands and the handlers’ enable state.
FooBarSelectionListener.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	...
	@Override
	public void selectionChanged(SelectionChangedEvent event) {
		requestRefresh();
	}
 
	protected void requestRefresh() {
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		IEvaluationService evaluationService = (IEvaluationService) window.getService(IEvaluationService.class);
		if (evaluationService != null) {
			evaluationService.requestEvaluation(PROPERTY_NAMESPACE + "." + PROPERTY_CAN_FOO);
			evaluationService.requestEvaluation(PROPERTY_NAMESPACE + "." + PROPERTY_CAN_BAR);
		}
	}
}

If you would run the application now, the commands in the toolbar would still be disabled, as their state never changes because our property tester always returns true for both variables. I suppose this must be a bug on Eclipse side, but I’m not certain yet. I doesn’t matter anyways, because FooBarPropertyTester class won’t stay that simple.

So, now that we listen to selection changes on the viewer, let’s modify the FooBarPropertyTester to enable the “Foo” command while the “One” element is selected in the viewer and to enable the “Bar” command while “Two” is selected.
FooBarPropertyTester.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import static de.rowlo.rcp.cce.foobar.FooBarSelectionListener.findViewer;
 
import org.eclipse.core.expressions.PropertyTester;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.Viewer;
 
import de.rowlo.rcp.cce.app.View;
 
public class FooBarPropertyTester extends PropertyTester {
	public static final String PROPERTY_NAMESPACE = "de.rowlo.rcp.cce.foobar";
	public static final String PROPERTY_CAN_FOO = "canFoo";
	public static final String PROPERTY_CAN_BAR = "canBar";
 
	@Override
	public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
		if (PROPERTY_CAN_FOO.equals(property)) {
			return viewerSelectionContains(View.ID, "One");
		} else if (PROPERTY_CAN_BAR.equals(property)) {
			return viewerSelectionContains(View.ID, "Two");
		}
		return false;
	}
 
	protected boolean viewerSelectionContains(String viewerId, Object object) {
		Viewer viewer = findViewer(viewerId);
		if (viewer != null &amp;&amp; object != null) {
			ISelection selection = viewer.getSelection();
			if (selection instanceof IStructuredSelection &amp;&amp; !selection.isEmpty()) {
				IStructuredSelection sel = (IStructuredSelection) selection;
				return sel.toList().contains(object);
			}
		}
		return false;
	}
}

The changes encompass a static import and the new method boolean viewerSelectionContains(String, Object). The test returns now true if the selected element is “One” for the property “canFoo” or “Two” if the property is “canBar”. In any other case the test fails. This leads to the command handlers being enabled as expected everytime the selection changes.

This screen shows the result of the above work.

Final Words

If you were looking for a way to enable your command handlers a more customized way then you should have found what you were looking for with this example. If you think I missed something, feel free to comment this post. Thanks for reading.

The example is available for download here: de.rowlo.rcp.cce.zip.

Part 2 Of This Example

As a next step this example is extended to support changing labels and tool tips of the commands depending on the viewer’s selection.

19 comments to Eclipse Tips & Tricks: Property Testers with Command Core Expressions

Leave a Reply

  

  

  

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>