Jun 03

This is the third part of my blog series on the Eclipse Commands framework. Please see Part One for an outline of topics covered in the various blogs.

Limiting visibility of Commands

A powerful feature of the Commands framework is that you can declare in the plugin.xml when a Command should be visible (or rather when specific menu contributions should be visible) and when it should be enabled. This is done by using what is called core expressions.


Simple visibleWhen definition

Let us take a simple example first: we want to limit the Annotate eBook command (from the example in Part Two: Quick Review) to only show in the context menu if one item is selected in the view. If no item is selected, or more than one, it must not appear in the menu.

  • First select the popup:org.eclipse.ui.popup.any?after=additions menu contribution extension element
  • Now select the Annotate command element, right-click and choose New -> visibleWhen
  • Set the checkEnabled value to false (setting to true will use the enabled state of the command to determine the visibility)
  • Right-click on the visibleWhen element, and choose New -> with
  • Set the variable attribute to “activeMenuSelection“. “activeMenuSelection” is a known expressions variable that will get resolved when the platform must populate the context menu. Se the list of other known expression variables.
  • Right-click on the activeMenuSelection(with) element and choose New -> count
  • Set the value attribute for count to “1“.

    We have now set up the context-menu contribution to only show the Annotate command when one and only one item in the view is selected. That was not so difficult, was it? I remember spending hours on creating nice selection behaviour helper classes for IActionDelegate-based frameworks, and still not getting the initial visibility or enabling of the actions in the menus right, due to constraints with that approach.

    The menu contributions extension in the plugin.xml now looks like:



       <extension
             point="org.eclipse.ui.menus">
          <menuContribution
                locationURI="menu:file?before=quit">
             <command
                   commandId="com.richclientgui.myreader.commands.OpenEBook"
                   mnemonic="O"
                   style="push">
             </command>
             <command
                   commandId="com.richclientgui.myreader.commands.AnnotateEBook"
                   mnemonic="A"
                   style="push">
             </command>
          </menuContribution>
          <menuContribution
                locationURI="popup:org.eclipse.ui.popup.any?after=additions">
             <command
                   commandId="com.richclientgui.myreader.commands.AnnotateEBook"
                   label="Annotate"
                   mnemonic="A"
                   style="push">
                <visibleWhen
                      checkEnabled="false">
                   <with
                         variable="activeMenuSelection">
                      <count
                            value="1">
                      </count>
                   </with>
                </visibleWhen>
             </command>
          </menuContribution>
       </extension>



    Expression Definitions

    Before I delve more deeply into even more interesting visibleWhen capabilities, first a very nifty tip on core expressions: it is possible to declare a specific expression definition, and then just refer to that definition from your visibleWhen, activeWhen, etc elements.

    We do this with the org.eclipse.core.expressions.definitions extension. To define an expression that checks that only one item is selected:

  • Add the org.eclipse.core.expressions.definitions extension (click Add… on Extensions tab of plugin manifest editor, you might need to uncheck the “Show only extension points from the required plug-ins” checkbox)
  • Give a proper id to your definition, e.g. expression.onlyOneItemSelected
  • Right-click on the expression.onlyOneItemSelected element and choose New -> with
  • Set the variable attribute to “activeMenuSelection“.
  • Right-click on the activeMenuSelection(with) element and choose New -> count
  • Set the value attribute for count to “1“.

    You might have noticed it is the same steps as those followed for a simple visibleWhen definition above.
    For those who rather do it directly in the plugin.xml, here is the org.eclipse.core.expressions.definitions extension declaration:





       <extension
             point="org.eclipse.core.expressions.definitions">
          <definition
                id="expression.onlyOneItemSelected">
             <with
                   variable="activeMenuSelection">
                <count
                      value="1">
                </count>
             </with>
          </definition>
       </extension>


    How do I use the expression definition?

    What do you gain with first defining an expression? Well, you can reuse this definition in multiple places in your plugin.xml if you need the same expression, whether it is for a visibleWhen, activeWhen or enabledWhen declaration.

    To replace our expression for our context menu contribution with the definition, you simply do the following:

  • Select the activeMenuSelection(when) element under the popup:org.eclipse.ui.popup.any?after=additions menu contribution’s visibleWhen element, right-click and select Delete
  • Right-click again on the visibleWhen element and choose New -> reference
  • Set the definitionId attribute to expression.onlyOneItemSelected (i.e. the id of the expression definition you want to use to evaluate whether the menu contribution must be visible or not)

    If you run the application now, it should still behave the same as before we used our own definition, but now we are ready for a much more complex playing field. Underneath is a screenshot of how the extension declarations should look in the Plugin Editor.

    Extensions Commands 4


    To infinity and beyond: the power of expressions

    We have seen a very basic expression up to now. You should have seen a number of other possible items to choose from when you selected New -> with in the steps above. I list them below. These can all be used to build up complex expressions that gets evaluated at runtime to determine whether a specific menu contribution should be visible or not, if used in conjunction with a visibleWhen element.
    Possible Expression Elements

    Element Description
    adapt Use to adapt the object in focus (i.e. the object passed to the expression) to the specified type. See Using adapt for more information.
    and AND the results from evaluating the sub-elements
    count Use to count the number of elements in a collection. The value attribute be an integer or a supported wildcard. See Using count for more information.
    equals Use to check equality of the focussed object with the specified value attribute
    instanceof Use to perform an instanceof check on the object in focus. See Using instanceof for more information.
    iterate Use to iterate over a collection, if the focussed object is of type java.util.Collection. See Using iterate for more information.
    not NOT the results from evaluating the sub-elements
    or OR the results from evaluating the sub-elements
    reference This is the element we use to refer to an expression defined under org.eclipse.core.expressions.definitions. The definitionId is the unique id of the expression definition to use for the evaluation.
    systemTest Use to test a system property, where property is the name of the property to get via System.getProperty, and value is the expected value to test against (as String value).
    test Use this element to test the value of a specific property on the focussed object. See Using test for more information.
    with Use to change the object to inspect or focus on to the object referenced by the variable attribute. See the list of available expression variables.
    resolve Resolve does almost the same as with, but it allows the use of a custom IVariableResolver, and passing the specified args as arguments to the variable resolver. More detail might be topic of a future blog post. I must admit, I have never used this one.

    Note on String values

    The value attribute for the above elements gets specified as some string. Here follows the extract from the Eclipse online help on how that String value might get converted:

    When specifying a value to be tested against any of these expressions, the value is assumed to be a string except for when the following conversions are successful:

  • the string “true” is converted into Boolean.TRUE
  • the string “false” is converted into Boolean.FALSE
  • if the string contains a dot, the interpreter tries to convert the value into a Float object
  • if the string only consists of numbers, the interpreter converts the value into an Integer object
    the conversion into a Boolean, Float, or Integer can be suppressed by surrounding the string with single quotes.


    Using adapt

    Adapt is used to adapt the object in focus (you can see this as the object provided or specified by the parent element) to the type specified its the type attribute. Any sub-elements of the adapt element will be evaluated against the adapted object. Read up about IAdapterFactory and IAdaptable interfaces for more information about adaption.
    If the object can be adapted, the adapt evaluate to true. E.g. we can define an expression for evaluating if something adapts to the ICountable interface.





       <extension
             point="org.eclipse.core.expressions.definitions">
          <definition
                id="isCountable">
             <adapt
                   type="org.eclipse.core.expressions.ICountable">
             </adapt>
          </definition>
       </extension>



    Using instanceof

    This is used to perform an instanceof check on the object in focus. We can for example enhance our Annotate eBook menu contribution in the File menu to only display if at least on of the selected items is an instance of java.lang.String (iterate gets discussed here):





       <extension
             point="org.eclipse.ui.menus">
          <menuContribution
                locationURI="menu:file?before=quit">
             <command
                   commandId="com.richclientgui.myreader.commands.OpenEBook"
                   mnemonic="O"
                   style="push">
             </command>
             <command
                   commandId="com.richclientgui.myreader.commands.AnnotateEBook"
                   mnemonic="A"
                   style="push">
                <visibleWhen
                      checkEnabled="false">
                   <with
                         variable="selection">
                      <iterate
                            ifEmpty="false"
                            operator="or">
                         <instanceof
                               value="java.lang.String">
                         </instanceof>
                      </iterate>
                   </with>
                </visibleWhen>
             </command>
          </menuContribution>



    Using iterate

    Iterate can be used if the object in focus is a java.util.Collection (e.g. when with is used in the parent element and the variable attribute is set to “selection“). It iterates through the collection, and evaluate the sub-elements against each item in the collection.

    The operator attribute can be set to and or or. For and, every item must cause the sub-elements of the expression to evaluate to true, and for or, at least one must evaluate to true.

    The ifEmpty attribute can be set to true or false. If true, then the iterate expression will evaluate to true for empty collections, else to false.

    See the example under the instanceof section.


    Using count

    The count element’s value attribute can take an integer value as parameter if the collection needs to contain the exact number of items. However, the following wildcards are also allowed:

    * Any number of items
    ? Zero or one item
    + One ore more items
    ! No items

    See our first simple visibleWhen example for usage.


    Using test

    Test can be used to evaluate the state (i.e. value) of a specific property of the focussed object. The test expression can evaluate to true, false or not-loaded if the property tester (implemented by a Java class) is not loaded.

    property specifies the name of the property of the object to test
    args specifies additional arguments to pass to the property tester
    value the value we expect the property to be
    forcePluginActivation if this is set to true, the plugin that contributes the property tester class will be loaded to enable the evaluation, if it is not loaded yet. However, this loading will only happen if the evaluation context used allows plugin activation. What does this mean: do not depend that when you set this flag to true, the plugin containing the property tester code will actually be loaded.

    In a future blog I will discuss how to implement your own property testers; quite a powerful feature. For now, let me just point you to some property testers available for your enjoyment. You can follow the links for the detailed code level information, or else have a look at the Propery Testers section in the Command Core Expressions wiki page for a summary.

  • PlatformPropertyTester
  • ResourcePropertyTester
  • FilePropertyTester
  • ProjectPropertyTester
  • ResourceMappingPropertyTester
  • ActivityPropertyTester
  • OpenPerspectivePropertyTester


    Core Expression Variables

    I was planning to list the most common variables available for core expressions here and discuss some of them, but time caught up with me. For now I will point you to:

  • The Variables and the Command Framework section in the Command Core Expressions wiki page.
  • The ISources.java file that list these variables.

    Note: if you use the selection variable, you need to make sure the View that you are expecting a selection from is a SelectionProvider for the platform by registering it as a selection provider, e.g. by calling getSite().setSelectionProvider(viewer).

    Coming soon…

  • What about enabling and disabling Commands? (enabledWhen, activeWhen)
  • More on using object properties to determine visibility and enablement of Commands (propertyTesters)
  • If you don’t like XML: how to tune your Commands from the Java side of things (the programmatic approach)

    References

  • Eclipse.org Commands Framework Article in bugzilla
  • Article on Eclipse Commands Framework, by Marc R. Hoffmann
  • Eclipse.org Wiki on Command Framework
  • Eclipse Commands Tutorial, by Lars Vogel
  • Eclipse.org Wiki on Core Expressions
  • Eclipse.org Wiki on Menu Contributions
  • Eclipse.org Wiki on mapping from old to new menu contribution extension points

    Enough said.

  • Leave a Reply

    You must be logged in to post a comment.

    preload preload preload