ExpressionsExpression

Expressions

The expression sub-language is a syntactical mixture of Java and OCL. This documentation provides a detailed description of each available expression. Let us start with some simple examples.

Accessing a property:

myModelElement.name

Accessing an operation:

myModelElement.doStuff()

simple arithmetic:

1 + 1 * 2

boolean expressions (just an example:-)):

!('text'.startsWith('t') && ! false)

There are several literals for built-in types:

Like OCL, the Xpand expression sub-language defines several special operations on collections. However, those operations are not members of the type system, therefore you cannot use them in a reflective manner.

There are two different forms of conditional expressions. The first one is the so-called if expression . Syntax:

condition ? thenExpression : elseExpression

Example:

name != null ? name : 'unknown'

Alternatively, you also could write:

if name != null then
  name 
else
  'unknown'
    

The other one is called switch expression . Syntax:

switch (expression) {
   (case expression : thenExpression)*
   default : catchAllExpression
}

The default part is mandatory, because switch is an expression, therefore it needs to evaluate to something in any case.

Example:

switch (person.name) {
   case 'Hansen' : 'Du kanns platt schnacken'
   default : 'Du kanns mi nech verstohn!'
}

There is an abbreviation for Boolean expressions:

switch {
   case booleanExpression : thenExpression
   default : catchAllExpression
} 

Expressions and functional languages should be free of side effects as far as possible. But sometimes there you need invocations that do have side effects. In some cases expressions even do not have a return type (i.e. the return type is Void). If you need to call such operations, you can use the chain expression.

Syntax:

anExpr ->
anotherExpr ->
lastExpr 

Each expression is evaluated in sequence, but only the result of the last expression is returned.

Example :

person.setName('test') ->
person

This chain expression will set the name of the person first, before it returns the person object itself.

The new expression is used to instantiate new objects of a given type:

new TypeName

Note that often create extensions are the better way to instantiate objects when used for model transformations.

Sometimes you don't want to pass everything down the call stack by parameter. Therefore, we have the GLOBALVAR expression. There are two things you need to do, to use global variables.

The expressions language supports multiple dispatching . This means that when there is a bunch of overloaded operations, the decision which operation has to be resolved is based on the dynamic type of all parameters (the implicit 'this' included).

In Java only the dynamic type of the 'this' element is considered, for parameters the static type is used (this is called single dispatch).

Here is a Java example:

class MyType {
   boolean equals(Object o) {
      if (o instanceof MyClass) {
         return equals((MyClass)o);
      }
      return super.equals(o);
   }
   boolean equals(MyType mt) {
      //implementation...
   }
} 

The method equals(Object o) would not have to be overwritten, if Java would support multiple dispatch.

The expression language is statically type checked. Although there are many concepts that help the programmer to have really good static type information, sometimes. One knows more about the real type than the system. To explicitly give the system such an information casts are available. Casts are 100% static, so you do not need them, if you never statically typecheck your expressions!

The syntax for casts is very Java-like:

((String)unTypedList.get(0)).toUpperCase()

When the name of a metamodel property conflicts with an Xpand or Xtend keyword, the conflict is resolved in favour of the keyword. To refer to the metamodel property in these cases, its name must be preceded by a '^' character.

Example:

private String foo(Import ^import) : ^import.name;