2017 June Release

OperatorsPermanent link for this heading

app.ducx expression language supports a wide range of operators.

Assignment operatorsPermanent link for this heading

Assignment operators allow you to set property and variable values. The next table contains a list of supported assignment operators.

Operator

Description

=

The = operator is used for simple assignments. The value of the right operand is assigned to the left operand.

+=

Both operands are added, and the result is assigned to the left operand. The += operator can be used with strings, numeric data types, currencies and lists.

-=

The right operand is subtracted from the left operand, and the result is assigned to the left operand. The -= operator can be used with numeric data types, currencies, lists and dictionaries.

*=

Both operands are multiplied, and the result is assigned to the left operand. The *= operator can be used with numeric data types, currencies, lists and dictionaries.

/=

The left operand is divided by the right operand, and the result is assigned to the left operand. The /= operator can be used with numeric data types, currencies, lists and dictionaries.

%=

A modulus operation is carried out, and the result is assigned to the left operand. The %= operator can only be used with numeric data types, currencies, lists and dictionaries.

<<=

The <<= is used for character- and bitwise shifting to the left. The <<= operator can be used with strings, integers, currencies and lists.

>>=

The >>= is used for character- and bitwise shifting to the right. The >>= operator can be used with strings, integers, currencies and lists.

Table 37: Assignment operators

Example

Order @order;
Customer @customer;

// A simple assignment operation
@order.orderdate = coonow;

// Adding an element to a list
@customer.customerorders += @order;

// Adding an element to a list only if it is not part of the list already
@customer.customerorders *= @order;

Logical operatorsPermanent link for this heading

Logical operators are implemented to support short circuit evaluation semantics. The right operand is only evaluated if the result of the evaluation is not determined by the left operand already. The next table shows a list of the supported logical operators.

Operator

Description

and (alternatively &&)

The and operator indicates whether both operands are true. If both operands have values of true, the result has the value true. Otherwise, the result has the value false. Both operands are implicitly converted to BOOLEAN and the result data type is BOOLEAN.

or (alternatively ||)

The or operator indicates whether either operand is true. If either operand has a value of true, the result has the value true. Otherwise, the result has the value false. Both operands are implicitly converted to BOOLEAN and the result data type is BOOLEAN.

not (alternatively !)

The expression yields the value true if the operand evaluates to false, and yields the value false if the operand evaluates to true. The operand is implicitly converted to BOOLEAN, and the data type of the result is BOOLEAN.

Table 38: Logical operators

Example

if (@orderstate == OrderState(OS_SHIPPED) and @orderdate != null or
  @orderstate == OrderState(OS_COMPLETED) and @invoice == null) {
  throw coort.SetError(#InvalidProcessingState, null);
}

Calculation operatorsPermanent link for this heading

Table 39 contains a list of supported calculation operators.

Operator

Description

+ - * / %

The +, -, *, / and % operators are supported for numeric data types and lists. +, -, * and / are also supported for currencies (* and / need one integer or float operand). Additionally, the + operator can be used to concatenate strings.

When used with lists, the following semantic applies:

+ (concatenation): The right operand is concatenated to the end of the left operand.

- (difference): Each element from the right operand is removed from the left operand.

* (union): Each element from the right operand is appended to the left operand if the element does not occur in the left operand.

/ (symmetric difference): The resulting list is the union of the difference of the left and the right operand and the difference of the right and the left operand: a / b == (a - b) * (b - a) == (a * b) - (b % a).

% (intersection): The resulting list is the list of elements that exist in both the left and the right operand.

Note: Each element in a list is treated as an individual element, even if the list contains other elements with the same value. So be careful if you use operators with lists that are not unique.

Since there are two elements "2" in the left operand, these expressions are true:

[1, 2, 2] - [2] == [1, 2];

[1, 2, 2] * [2] == [1, 2, 2];

When used with dictionaries, the following semantic applies:

- (difference): Each entry from the right operand is removed from the left operand (regardless of the value of the entry)

* (union): Each entry from the right operand is appended to the left operand if the entry does not occur in the left operand.

/ (symmetric difference): The resulting dictionary is the union of the difference of the left and the right operand and the difference of the right and the left operand: a / b == (a - b) * (b - a) == (a * b) - (b % a).

% (intersection): The resulting dictionary contains the entries that exist in both the left and the right operand. The values are taken from the left operand.

++

The ++ increment operator is a unary operator that adds 1 to the value of a scalar numeric operand. The operand receives the result of the increment operation. You can put the ++ before or after the operand. If it appears before the operand, the operand is incremented. The incremented value is then used in the expression. If you put the ++ after the operand, the value of the operand is used in the expression before the operand is incremented.

--

The -- decrement operator is a unary operator that subtracts 1 from the value of a scalar numeric operand. The operand receives the result of the decrement operation. You can put the -- before or after the operand. If it appears before the operand, the operand is decremented. The decremented value is then used in the expression. If you put the -- after the operand, the value of the operand is used in the expression before the operand is decremented.

<<

The << is used for character- and bitwise shifting to the left. The << operator can be used with strings, integers, currencies and lists. When used with stings, the right operand specifies the number of characters removed from the beginning of the string. When used with lists, the right operand specifies the number of elements to be removed from the top of the list.

>>

The >> is used for character- and bitwise shifting to the right. The >> operator can be used with strings, integers, currencies and lists. When used with strings, the right operand specifies the number of spaces inserted on the left side of the string. When used with lists, the right operand specifies the number of elements to be removed from the end of the list.

Table 39: Calculation operators

Example

@aaa = ["John", "James", "Jim", "Jamie"];
@bbb = ["Jamie", "Jim"];
// Check if every element of list @bbb is included in list @aaa.
// You have to sort the both operands of the compare

// since for % the order is taken from the first operand @aaa:

sort(@aaa % @bbb) == sort(@bbb);

// So it is more efficient to use the difference:
@bbb - @aaa == [];

// Check if the last change of an object was carried out on the same date it was
// created. Using the "%86400" operation, the time portion of the datetime

// property "COOSYSTEM@1.1:objchangedat" is set to "00:00:00" in order to compare

// the date portion only using the "==" operator.

objcreatedat % 86400 == objchangedat % 86400


Note: If two different currencies are added or subtracted an implicit conversion is carried out. Following evaluation order is defined: The conversion table of the transaction variable TV_CURRCONVTAB is used. If TV_CURRCONVTAB is not available, the conversion table of the left operand is used. If not available, the conversion table of the right operand is used. Otherwise, an error is generated.

Examples for list operatorsPermanent link for this heading

The +, -, *, / and % operators (concatenation, difference, union, symmetric difference, intersection) are supported for lists. The following example shows how list operators work.

Example

[]+[1] == [1];
[1, 2, 3] + [2, 3, 4] == [1, 2, 3, 2, 3, 4];
[1, 2, 3] + [] == [1, 2, 3];

[1, 2, 3] - [2, 3, 4] == [1];
[1, 2, 3] - [1, 2, 3] == [];
[1, 2, 2, 3] - [1, 2, 3] == [2];
[1, 2, 3] - [3, 2, 1] == [];
[1, 2, 3] - [4, 5, 6] == [1, 2, 3];
[1, 2, 2] - [2, 3, 4] == [1, 2]
[] - [1] == [];

[1, 2, 3] * [2, 3, 4] == [1, 2, 3, 4];
[1, 2, 2] * [1, 2, 2, 3, 3, 3, 4] == [1, 2, 2, 3, 3, 3, 4];
[] * [1, 2, 2] == [1, 2, 2];
[1, 2, 3] * [] == [1, 2, 3];
[1, 2, 3] * [3, 2, 1] == [1, 2, 3];
[1, 2, 2] * [2, 3, 4] == [1, 2, 2, 3, 4];

[1, 2, 3] / [4, 5, 6] == [1, 2, 3, 4, 5, 6];
[1, 2, 3] / [2, 3, 4] == [1, 4];
[1, 2, 3] / [1, 2, 3] == [];
[1, 2, 3] / [3, 2, 1] == [];
[1, 2, 2] / [2, 3, 4] == [1, 2, 3, 4]

[1, 2, 3, 4] % [2, 3, 4] == [2, 3, 4];
[1, 2, 3] % [2, 3, 4] == [2, 3];
[1, 2, 3] % [4, 5, 6] == [];
[1, 2, 3] % [3, 2, 1] == [1, 2, 3];
[] % [4, 5, 6] == [];

Examples for dictionary operatorsPermanent link for this heading

The -, *, / and % operators (difference, union, symmetric difference, intersection) are supported for dictionaries. These operators work on an element level, the value of a dictionary entry is not relevant. The left operand dominates. The following example shows how dictionary operators work.

Example

({}) - ({}) == ({});
({}) - ({ a: 1, b: "x", c: true }) == ({});
({ a: 1, b: "x", c: true }) - ({}) == ({ a: 1, b: "x", c: true });
({ a: 1, b: "x", c: true }) - ({ a: 1, b: "x", c: true }) == ({});
({ a: 1, b: "x", c: true }) - ({ b: 2, c: "x", d: true }) == ({ a: 1 });
({ a: 1, b: "x", c: true }) - ({ a: 2, b: "x", c: 1 }) == ({});
({ a: 2, b: "x", c: 1 }) - ({ a: "x" }) == ({ b: "x", c: 1 });

({}) * ({}) == ({});
({}) * ({ a: 1, b: "x", c: true }) == ({ a: 1, b: "x", c: true });
({ a: 1, b: "x", c: true }) * ({}) == ({ a: 1, b: "x", c: true });
({ a: 1, b: "x", c: true }) * ({ a: 1, b: "x", c: true }) == ({ a: 1, b: "x", c: true });
({ a: 1, b: "x", c: true }) * ({ b: 2, c: "x", d: true }) ==
                                                    ({ a: 1, b: "x", c: true, d: true });
({ a: 1, b: "x", c: true }) * ({ a: 2, b: "x", c: 1 }) == ({ a: 1, b: "x", c: true });
({ a: 2, b: "x", c: 1 }) * ({ a: "x" }) == ({ a: 2, b: "x", c: 1 });

({}) / ({}) == ({});
({}) / ({ a: 1, b: "x", c: true }) == ({ a: 1, b: "x", c: true });
({ a: 1, b: "x", c: true }) / ({}) == ({ a: 1, b: "x", c: true });
({ a: 1, b: "x", c: true }) / ({ a: 1, b: "x", c: true }) == ({});
({ a: 1, b: "x", c: true }) / ({ b: 2, c: "x", d: true }) == ({ a: 1, d: true });
({ a: 1, b: "x", c: true }) / ({ a: 2, b: "x", c: 1 }) == ({});
({ a: 2, b: "x", c: 1 }) / ({ a: "x" }) == ({ b: "x", c: 1 });

({}) % ({}) == ({});
({}) % ({ a: 1, b: "x", c: true }) == ({});
({ a: 1, b: "x", c: true }) % ({}) == ({});
({ a: 1, b: "x", c: true }) % ({ a: 1, b: "x", c: true }) == ({ a: 1, b: "x", c: true });
({ a: 1, b: "x", c: true }) % ({ b: 2, c: "x", d: true }) == ({ b: "x", c: true });
({ a: 1, b: "x", c: true }) % ({ a: 2, b: "x", c: 1 }) == ({ a: 1, b: "x", c: true });
({ a: 2, b: "x", c: 1 }) % ({ a: "x" }) == ({ a: 2 });

Comparison operatorsPermanent link for this heading

Comparison operators allow you to compare two operands. The next table provides a summary of the supported comparison operators. The data type of the result is always BOOLEAN.

Operator

Description

==

The equality operator compares two operands and indicates whether the value of the left operand is equal to the value of the right operand. The equality operator has a lower precedence than the relational operators (<, <=, >, >=).

!= (alternatively <>)

The inequality operator compares two operands and indicates whether the value of the left operand is not equal to the value of the right operand. The inequality operator has a lower precedence than the relational operators (<, <=, >, >=).

<

The relational operator < compares two operands and indicates whether the value of the left operand is less than the value of the right operand.

<=

The relational operator <= compares two operands and indicates whether the value of the left operand is less than or equal to the value of the right operand.

>

The relational operator > compares two operands and indicates whether the value of the left operand is greater than the value of the right operand.

>=

The relational operator >= compares two operands and indicates whether the value of the left operand is greater than or equal to the value of the right operand.

<=>

The relational operator <=> compares two operands. It returns -1 if the left value is lower and +1 if the left value is greater than the right value. If the values are equal, the result is 0.

contains

The contains operator determines whether left operand contains the right operand. This operator can be used with string operands. It may be preceded by the not keyword.

like

The like operator determines whether the left string matches the right string. The % and _ wildcards can be used in the right string operand. The like operator can be preceded by the sounds keyword for a phonetic comparison. Furthermore, it can also be preceded by the not keyword.

in

The in operator determines whether the value of the left operand is an element of the list provided in the right operand. The in operator can also be used with a list in the left operand. It may be preceded by the not keyword.

When using lists in both operands, the semantic is:

[a1, a2, ... an] in [b1, b2, ... bm]

->

(a1 == b1 or a1 == b2 or ... or a1 == bm) or
(a2 == b1 or a2 == b2 or ... or a2 == bm) or
       .
       .
       .
(an == b1 or an == b2 or ... or an == bm)

This means, that the expression is true, if any element from the left operand is in the list of the right operand.

Note: When the left operand is null (i.e. an empty list) the evaluation of the in operator is true since an empty list is always part of any other list.

includes

The includes operator determines whether the value of the right operand is an element of the list provided in the left operand. It may be preceded by the not keyword.

When using lists in both operands, the semantic is:

[a1, a2, ... an] includes [b1, b2, ... bm]

->

(a1 == b1 or a1 == b2 or ... or a1 == bm) and
(a2 == b1 or a2 == b2 or ... or a2 == bm) and
       .
       .
       .
(an == b1 or an == b2 or ... or an == bm)

This means, that the expression is true, if all elements from the right operand are in the list of the left operand.

Note: When the right operand is null (i.e. an empty list) the evaluation of the indluces operator is true since an empty list is always part of any other list.

between and

The between and operator determines whether the value of the first operand is in the range between the values of the operands provided after the keywords between and and.

If the first operand is a list, then all values of the list must be between the second and the third operand.

is null

The is null operator returns true if the value of the left operand is undefined.

Table 40: Comparison operators

Example

if (@points < 100) {
  @memberstatus = "MS_SILVER";
}
else if (@points between 100 and 1000) {
  @memberstatus = "MS_GOLD";
}
else {
  @memberstatus = "MS_PLATINUM";
}

// If @memberstatus is null this evaluates to true
if (@memberstatus in ["MS_GOLD", "MS_PLATINUM"]) {
  @expressshipping = true;
}

if (@nickname like "Bob%" or @nickname in ["Dick", "Rob"]) {
  @firstname = "Robert";
}


Note:
When comparing aggregates or dictionaries, the values of all attributes or entries are compared. The comparison is recursive for nested values.

Note: Aggregate types can specify a comparator method in COOSYSTEM@1.1:typecompare. When comparing such aggregates this method is used to calculate the result of the comparison. Standard comparison of aggregates only allow a check for equality of values (operators ==, != , in, and includes). Using COOSYSTEM@1.1:typecompare allows implementation of greater or less operators for aggregates. If no COOSYSTEM@1.1:typecompare is specified, the default comparison for aggregats uses the properties in COOSYSTEM@1.1:typesort first, then the properties in COOSYSTEM@1.1:typecompattrs.

Note: Objects are compared in an internal order.

Note: Dictionaries only allow a check for equality of values (operators ==, != , in, and includes).

Note: Contents and COM interfaces cannot be compared by their value, when comparing using the operators ==, != , in, and includes, the internal identity of these objects is used.

Note: String operands are compared using the setting COOSYSTEM@1.1:domaincisqry in your current domain object. The default for this setting is true, meaning that comparison is case insensitive by default. This is also relevant for min/max/sort/unique/find and the list operators -, *, /, and %.

Conditional operatorPermanent link for this heading

The conditional operator ?: has three operands. It tests the result of the first operand, and then evaluates one of the other two operands based on the result of the evaluation of the first operand. If the evaluation of the first operand yields true, the second operand is evaluated. Otherwise, the third operand is evaluated.

Example

@orders = (@customer != null) ?
  @customer.customerorders[objname] :
  null;

Selection operatorPermanent link for this heading

The selection operator [] can be used for the following purposes:

  • as a list constructor to define a list of values,
  • to select elements from a list of values
  • to filter elements of a list,
  • to specify a parameter as the return value when invoking a use case, and
  • for calculated identifiers (see chapter “Calculated identifiers”).

Example

// Constructing an empty list
@productcategories = [];

// Constructing a list of string values
@productcategories = ["Fish", "Meat", "Poultry"];

// Selecting elements from a list
// The result is a single element
@fish = @productcategories[0];
@meat = @productcategories[1];

// Selecting multiple elements from a list
//
The result is a list again
@nofish = @productcategories[1,2];

// Selecting elements starting from the end of the list by specifying negative indices
@poultry = @productcategories[-1];

// Example for filtering a list: This expression returns the orders that
// do not have a valid order date.

// The result is again a list if more than one item is selected
@customer.customerorders[orderdate is null];

// Selecting a sub list
//
The result is a list again
@nofish = @productcategories[1:2];

// Selecting a sub list with negative elements
//
The result is a list again
@nofish = @productcategories[-2:-1];

// Specifying a parameter as the return value when invoking a use case
@neworder = #Order.ObjectCreate()[2];

// Results in the method object of the call
@meth = #Order.ObjectCreate()[...];

// Results in all entries of the customization point CPSymbols
@list = coouser.CPSymbols()[...];

$-operatorPermanent link for this heading

By default, the Fabasoft Folio Kernel tries to interpret identifiers as references when evaluating expressions. In order to use an identifier that could also be a reference as name, it must be prefixed with $.

Note: The Fabasoft app.ducx compiler will attempt to automatically insert the symbol $ when serializing the expression, if it can determine the context in which the identifier is used. If it can’t calculate a definitive type, you will receive a warning.

For the following example, assume that the local scope contains a dictionary. objname can only be used as a variable when prefixed with $.

Example

// Assuming the local scope contains a dictionary:
$objname = "Hello world";

// When omitting the "$", the expression is interpreted as follows:
this.COOSYSTEM@1.1:objname = "Hello world";

#-operatorPermanent link for this heading

If you need to retrieve a component object in an expression, you must prefix its reference with #. In order to use an identifier that could also be a variable as reference, it must be prefixed with #.

Note: The Fabasoft app.ducx compiler will attempt to automatically full qualify an identifier to a reference when serializing the expression, if it can determine the context in which the identifier is used. If it can’t calculate a definitive type, you will receive a warning.

For instance, when referring to a property definition or an object class, you must prefix the reference with # in order to get the corresponding component object.

Example

// Accessing property definition COOSYSTEM@1.1:objname
@objnameprop = #objname;
@ordername = @order.GetAttributeValue(cootx, @objnameprop);

// Accessing object class APPDUCXSAMPLE@200.200:Order
@orderclass = #APPDUCXSAMPLE@200.200:Order;
@neworder = @orderclass.ObjectCreate();

// Accessing property of undeclared object
@neworder.#objname = "New Name";