Package org.eclipse.core.expressions


package org.eclipse.core.expressions
Application programming interfaces for the expression language.

Package Specification

The package provides API and implementation classes to define a unified XML expression language. The expression language isn't only for extension points, it can be used in any XML document that supports enablement expressions.

XML expression language

The XML expression language consists of the following predefined expression tags. The set is open and can be extended to customize the expression language.

Boolean operators

The expression language provides standard expressions for the Boolean operators and, or and not.

Instanceof expression

The most frequently used check in current extension points is one to test if an object conforms to a certain type. The common XML expression language provides a special XML element to represent instance of checks. A typical usage looks as follows:

<instanceof value="org.eclipse.jdt.core.IJavaElement"/>

The above expression tests, if the object under inspection (in most cases the element selected in the user interface) is of instance "org.eclipse.jdt.core.IJavaElement".

Test expression

Besides instance of checks the new expression language defines an extensible <test> element to support property testing. The <test> element is comparable to the <filter> element used in Platform/UI. The test element is used as follows:

<and>
  <instanceof value="org.eclipse.core.resources.IFile"/>
  <test property="org.demo.matchesPattern" value="*.html"/>
</and>

The above expression evaluates to true if the object under inspection is of type "org.eclipse.core.resources.IFile" and its file name matches the pattern "*.html". But who actually provides the code to do the name pattern test? Predefining a set of properties to test is too limiting. The set of tests must be open. Property testers are added to the system using a special extension point propertyTesters. The above matchesPattern property is added to the system in the following way:

<extension point="org.eclipse.core.expressions.propertyTesters">
    <propertyTester
        id="org.eclipse.jdt.ui.IResourceTypeExtender"
        type="org.eclipse.core.resources.IResource"
        namespace="org.demo"
        properties="matchesPattern, ...."
        class="org.eclipse....ResourcePropertyTester">
     </propertyTester>
</extension>

The major characteristics of the extensible test mechanism are:

  • types are enriched with new properties using a property tester, meaning that the code of the actual property test is provided by a different class.
  • a property tester implements a set of properties.
  • property testers and their properties are defined in XML as extension points. This is required to check if an extender provides a property without having to activate it (e.g. load the plug-in).
  • properties belong to a name space. This ensures that two sibling plug-ins can define the same property without causing any ambiguity. If a property is defined more than once for a name space then one of the testers is randomly chosen to test the property. Inheritance only works within the same name space. If, for example, there is a property isPublic defined in the namespace org.myNamespace for type org.eclipse.core.IMethod then this property will not override the property isPublic in the namespace org.yourNamespace for type org.eclipse.core.IMember
  • testing for an unknown property results in a core exception. This is a programming error.

The attributes of the propertyTester element have the following meaning:

  • id: a unique id
  • type: the type which gets "enriched" with new methods
  • namespace: the name space the properties belong to
  • properties: the comma separated list of properties provided by the tester.
  • class: the implementing class

The concrete implementation for the above property tester looks like this:

public class ResourcePropertyTester extends PropertyTester {
  private static final String PROPERTY_MATCHES_PATTERN= "matchesPattern"; //$NON-NLS-1$



  public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
    IResource resource= (IResource)receiver;
    if (PROPERTY_MATCHES_PATTERN.equals(method)) {
      String fileName= resource.getName();
      StringMatcher matcher= new StringMatcher((String)expectedValue, false, false);
      return expectedValue == null
          ? matcher.match(fileName)
          : matcher.match(filename) == ((Boolean)expectedValue).booleanValue();
    } else if (...) {
    }
    Assert.isTrue(false);
    return false;
  }
}

The string value provided by the value attribute is converted into a Java object using the following rules:

  • the string "true" is converted into Boolean.TRUE
  • the string "false" is converted into Boolean.FALSE
  • if the string contains a dot then the interpreter tries to convert the value into a Float object. If this fails the string is treated as a java.lang.String
  • if the string only consists of numbers then the interpreter converts the value in an Integer object.
  • in all other cases the string is treated as a java.lang.String
  • the conversion of the string into a Boolean, Float, or Integer can be suppressed by surrounding the string with single quotes. For example, the attribute value="'true'" is converted into the string "true"

Sometimes a property test needs additional arguments to determine the exact property to test. If this is the case the arguments can be passed using the additional args attribute. An example which validates a name using the method IWorkspace.validateName looks as follows:

<test property="org.demo.validateName" args="/org.eclipse.demo/A.java, FILE"/>

With expression

Test expressions don't allow to specify the object they inspect. They work on a default object, which for most extension points is the object selected in the user interface. However, the enablement logic of some extension points need to test other objects as well. For example a Java refactoring participant tests if the list of affected projects contains a project with the Java nature:

<with variable="affectedProjects">
    <iterate operator="or">
        <test property="org.demo.projectNature" value="org.eclipse.jdt.core.javanature"/>
    </iterate>
</with>

The plug-in that evaluates the extension point is responsible for providing the set of available variables. For example, the code that evaluates refactoring participants provides the follow variables:

  • selection: its value is a collection containing the objects to be refactored
  • affectedProjects: its value is a collection containing the projects affected by the refactoring
  • defaultVariable: will be used if no with expression element is active. Is an alias for the variable selection.

If the variable doesn't exist, the with expression will throw a core exception.

Resolve expression

The resolve expression is comparable to the with expression, but it allows resolving the variable dynamically and to pass additional arguments needed to resolve the argument. For example to resolve the plug-in descriptor for a specific plug-in the following XML element can be used:

<resolve variable="pluginDescriptor" args="org.eclipse.core.runtime">
    <test property="org.demo.isActive"/>
</adapt>

The actual resolving is delegated to the evaluation context used to evaluate the expressions. See below on how to evaluate an expression and how to create an evaluation context.

Adapt expression

The adapt expression can be used to adapt the object to be inspected to an object of a different type using the adapter mechanism provided by the platform. The example below adapts the object to be inspected to an IType and then checks if the type is public:

<adapt type="org.eclipse.jdt.core.IType">
    <test property="org.demo.isPrivate"/>
</adapt>

Like the with expression the adapt expression changes the object to inspect for all its children. The new object is the one returned from IAdapter.getAdapter(). If the adaption fails, the expression evaluates to false.

The adapt expression is implemented based on the IAdapterManager API hasAdapter(Object, String) and getAdapter(Object, String). This ensures that the right class loader is taken to convert the type name into a corresponding Class object. However, using this API requires that the adapter factory providing the actual adapter is registered in XML using the extension point "org.eclipse.core.runtime.adapters". Assuming that there is an adapter that converts resources with the extension .java into IType objects, the adapter must be declared in XML to make the above adapt expression work correctly. The corresponding adapter definition looks like this:

<extension point="org.eclipse.core.runtime.adapters">
  <factory 
    class="org.demo.MyAdapterFactory" 
    adaptableType="org.eclipse.core.resources.IFile">
    <adapter type="org.eclipse.jdt.core.IType"/>
  </factory>
</extension>

SytemTest expression

There is a special XML element to test system properties.

<systemTest property="os.name" value="Windows XP"/>
<systemTest property="os.version" value="5.1"/>

Dealing with collection of elements

Several expressions are evaluated on a collection of objects (for example refactoring participants, menu contributions, ...). Up to now, the iteration over collections was implicitly coded into the enclosing XML element, which isn't part of the expression itself. The new mechanism provides explicit expression elements to deal with collections of objects. The following element

<count value="*"/>

is used to check the number of objects in a collection, and the syntax of the attribute value is equal to the enablesFor attribute used for object contributions. To iterate over a collection, an element

<iterate operator="...">

is provided. The operator attribute can either be "and" or "or". It determines how the evaluation results of all objects in the list are combined. The default operator is "and". Using these expression the enablement of a typical contribution can be described as follows:

<with variable="selection">
<count value="+"/>
<iterate operator="and"/>
<adapt type="org.eclipse.core.resources.IFile">
<test property="matchesName" value="*.gif"/>
<test property="canDelete"/>
</adapt>
</iterate>
</with>

The expression only evaluates to true if the selection contains one or more objects and all objects fulfill the expression defined by the adapt element.

Enablement expression

XML expressions are mostly used to define the availability of an extension point contribution, but they can be used in any XML document that allows expressions. To separate the expression from other child elements the common expression language provides an enablement element. Its use is as follows:

<renameParticipant
id="launchConfigUpdater"
class="org.eclipse...LaunchConfigUpdater">
<enablement>
...
</enablement>
</renameParticipant>

Most of the time child expression will be combined using the and operator. To avoid deep nesting XML "and" will be the default for combining children. It can therefore be omitted. The same applies to the adapt, iterate, and enablement expression defined in the following sections.

Extension Point Schema

An extension point schema exists for the property tester extension point and for the expression language itself. The schema for the expression language can be included into other schema files using the following include element:

<include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/>

Converting XML elements into expressions

XML elements can be converted into corresponding expression objects using the class ExpressionConverter. If only expression tags from the common expression language are used, then the standard expression converter can be used. The following example converts the configuration element representing an enablement element into an expression:

IConfigurationElement enablementElement= ...;
Expression expression= ExpressionConverter.getDefault().perform(enablementElement);

Evaluating an expression

Expressions are evaluated by calling Expression.evaluate(...);. The evaluation context passed to the evaluate method has to be set up by the plug-in that reads an extension point. The plug-in is responsible to set up the default variable and all the other variable used in with expressions. The example below creates a special evaluation context and uses this context to evaluate an expression:


EvaluationContext context= new EvaluationContext(null, defaultVariable) {
    public Object resolveVariable(String name, Object[] args) throws CoreException {
        // do special resolving
    }
}

The actual evaluation is done by calling:


EvaluationResult= expression.evaluate(context);