2017 June Release

Structured programming with expressionsPermanent link for this heading

For your convenience, app.ducx expression language supports all common language constructs that you already know from most imperative programming languages.

ConditionsPermanent link for this heading

Syntax

if (expression) {
  ...
}
else if (expression) {
  ...
}
else {
  ...
}

switch (expression) {
case constant:
  ...
  break;
case constant:
  ...
  break;
default:
  ...
  break;
}


You can use if statements in app.ducx expression language. The if keyword must be followed by parentheses enclosing a conditional expression, and non-optional curly braces. An if block can be followed by multiple else if blocks and an optional else block.

Example

@orderstate = @order.orderstate;

if (@orderstate == OrderState(OS_PENDING)) {
  @order.ProcessPendingOrder();
}
else if (@orderstate == OrderState(OS_SHIPPED)) {
  @order.ProcessShippedOrder();
}
else {
  throw coort.SetError(#OrderAlreadyProcessed, null);
}

// lists as conditional expression are evaluated true, if the list contains at least
// one not null element

if (["", "", "a"]) {
  true;
}


Note: It is not necessary that OS_PENDING is explicitly casted (e.g. @orderstate == OS_PENDING works, too).

The switch - case - default statement can be used to evaluate the switch expression and execute the appropriate case.

Example

OrderState @orderstate;

switch (@orderstate){
case OS_PENDING:
  @state = 1;
  break;
case OS_SHIPPED:
  @state = 2;
  break;
default:
  @state = 0;
  break;
}


Note: Enumeration items like OS_PENDING are determined by @orderstate in the switch statement.

LoopsPermanent link for this heading

Syntax

for (expression) {
  ...
}

while (expression) {
  ...
}

do {
  ...
} while (expression);


The app.ducx expression language allows you to define loops with both a constant and a non-constant number of iterations. The common statement constructs of imperative programming languages are supported: for, while, and do-while loops. The blocks denoted by curly braces are required. The break statement can be used to exit a loop. The continue statement can be used to skip the remainder of the loop body and continue with the next iteration of the loop.

Note: continue inside a catch block does not apply to any enclosing loop (see ).

Example

float @totalvalue = 0;

// Iteration with index (inefficient)
for (integer @idx = 0; @idx < count(orderpositions); @idx++) {
  Product @product = @positions[@idx].product;
  @totalvalue += FLOAT(@product.unitprice.currvalue) *
    @positions[@idx].quantity;
}

currency @total = 0;

// Iteration with iterator (efficient)
for (OrderPosition @position : orderpositions) {
  Product @product = @position.product;
  if (@product != null) {
    @total += @product.unitprice * @position.quantity;
  }
}

integer @stock = @product.itemsinstock;
integer @threshold = @product.productionthreshold;

while (@stock <= @threshold) {
  @product.ProduceItem();
  @stock++;
}

OrderState @orderstate;

do {
  @order.ProcessOrder(&@orderstate);
} while (@orderstate != OS_COMPLETED);

Note: Avoid iterating through lists using a for loop with an index, since this is very inefficient in large lists.

Raising an errorPermanent link for this heading

Syntax

// Raising a custom error
throw coort.SetError(errormessage, argument);

// Rethrowing an exception
throw errorcode;


Using the throw keyword, app.ducx expression language allows you to raise an error:

  • If you want to rethrow an exception in an error handler (e.g. in a catch block), you just need to specify the error code of the exception after the throw keyword.
  • If you want to raise a custom error, you need to issue a call to the SetError function of the Fabasoft Folio Runtime.
  • Instead of using the SetError function you can also use the COOSYSTEM@1.1:RaiseError action, which allows you to pass in additional arguments for filling a formatting string. The throw keyword is not required when using COOSYSTEM@1.1:RaiseError.

Example

// Raising a custom error using a "throw" statement
throw coort.SetError(#InvalidInvoice, null);

// Raising a custom error using the COOSYSTEM@1.1:RaiseError action, assuming that the
// error message
APPDUCXSAMPLE@200.200:InvalidInvoice contains a formatting pattern like
// "Invoice '%s' (no. %d) is not valid!"

cooobj.RaiseError(#InvalidInvoice, cooobj.objname, cooobj.invoicenumber);

Error handlingPermanent link for this heading

Syntax

try {
  ...
}
catch (condition) {
  ...
}
finally {
  ...
}


app.ducx expression language supports try-catch-finally statement constructs for handling exceptions. A try block must be followed by at least one catch block, followed by an optional finally block.

If an exception occurs when processing the try block, the Fabasoft Folio Kernel tries to locate a catch block with a condition matching the error message object of the exception.

A catch block can have three distinct types of conditions:

  • An error message object can be specified to handle only matching exceptions.
  • A variable can be specified. If an exception occurs, the error code is stored in the specified variable, and the corresponding catch block is executed. The variable can be used for reading the error code and the corresponding error message, using coort.GetErrorText(errorcode) or coort.GetErrorMessage(errorcode)
  • The ... operator can be specified to handle all exceptions without taking into account the error code.

To continue the execution directly after the statement raising the error the keyword continue can be used inside the catch block.

The optional finally block is executed after the try and catch blocks have been processed, whether an exception occurred or not.

Example

try {
  User usr = #User.ObjectCreate(,,newaddress);
}
catch(#COOERR_INVADDR) {
  /*
   * handle an invalid object address format

   */

}
catch(@ex) {

  /*
   * handle any other error raised during object creation

   */

  string errortext = coort.GetErrorText(@ex);
  ErrorMessage errorobject = coort.GetErrorMessage(@ex);
}
finally {
  ...
}

Creating new transactions or opening a transaction scopePermanent link for this heading

Similar to the try statement for error handling it is possible to execute a block using a separate transaction context:

Example

try new transaction {
  // The statements in this block are executed in a new transaction context.
  // This new transaction
context is also available in the cootx built-in variable.
  Invoice @invoice = #Invoice.ObjectCreate();
  @invoice.APPDUCXSAMPLE_200_300_InitializeInvoice();
}
// After the try block an implicit Commit() or Abort() is executed on the new
// transaction context created for the try block.

// If the code in the try block throws an exception, Abort() is called, else

// Commit().

// If the implicit Commit() itself throws an exception this can be handeled by

// the catch block below.

catch (...) {
  // Here you can catch exceptions that occurred during the try block or during the
  // implicit Commit() after the try block
.
  // This code is executed in the original transaction context so the
  // changes made during the try block are not available any more.

}
finally {
  // Here you can perform some additional cleanup.
  // This code is executed in the origina
l transaction context.
}

If the new keyword is omitted, a transaction scope is opened. A transaction scope is a sub transaction of the current transaction. When a transaction scope is commited, the changes of that scope are propagated to the surrounding transaction. These changes are only persisted if the surrounding transaction context is commited.

Example

try transaction {
  // The statements in this block are executed in a new transaction scope.
  // This new transaction scope is also available in the cootx built-in variable.
  Invoice @invoice = #Invoice.ObjectCreate();
  @invoice.APPDUCXSAMPLE_200_300_InitializeInvoice();

}
// After the try block an implicit Commit() or Abort() is executed on the new
// transaction scope created for the try block.

// If the code in the try block throws an exception, Abort() is called, else

// Commit().

// Afterwards, the transaction scope is closed.

// If the implicit Commit() itself throws an exception this can be handeled by
// the catch block below.

catch (...) {
  // Here you can catch exceptions that occurred during the try block or during the
  // implicit Commit() after the try block
.
  // This code is executed in the original transaction scope so the
  // changes made during the try block are not available any more.

}
finally {
  // Here you can perform some additional cleanup.
  // This code is executed in the origina transaction scope.
}

Returning valuesPermanent link for this heading

Syntax

return expression;

The return statement can be used to stop the evaluation of an expression at any time. Each expression has a return value, which is calculated by the expression following the return keyword.

Example

if (@order != null) {
  return @order;
}

DirectivesPermanent link for this heading

A directive is a special statement that does not influence the semantic of the expression.

Syntax

%%NAME(parameters);

%%TRACEPermanent link for this heading

The %%TRACE directive can be used to conditionally write trace messages to the Fabasoft app.ducx Tracer (see chapter “Tracing in Fabasoft app.ducx projects”).

Syntax

%%TRACE(message);
%%TRACE(value);
%%TRACE(message, value);

Example

%%TRACE("Hello World!");
%%TRACE(cooobj);
%%TRACE("Current Object", cooobj);
%%TRACE(cooobj.objname + " locked?", cooobj.objlock.objlocked);

%%FAILPermanent link for this heading

The %%FAIL directive can be used to generate a failure. The message is written to the Fabasoft app.ducx Tracer and an error (EXPRERR_FAIL) is raised.

Note: Like the %%TRACE directive, the %%FAIL directive is only evaluated if trace mode is activated for your software component (see chapter “Tracing in Fabasoft app.ducx projects”).

Syntax

%%FAIL;
%%FAIL(message);

Example

%%FAIL;
%%FAIL("Unexpected!");

%%ASSERTPermanent link for this heading

The %%ASSERT directive can be used to check conditions. In case the condition returns false, a message is written to the Fabasoft app.ducx Tracer and an error (EXPRERR_ASSERT) is raised.

Note: Like the %%TRACE directive, the %%ASSERT directive is only evaluated if trace mode is activated for your software component (see chapter “Tracing in Fabasoft app.ducx projects”).

Syntax

%%ASSERT(condition);
%%ASSERT(message, condition);
%%ASSERT(message, expectedvalue, actualvalue);

Example

%%ASSERT(cooobj.objlock.objlocked);
%%ASSERT("'cooobj' should not be locked.", cooobj.objlock.objlocked);

@expect = "Test";
@actual = cooobj.objname;
%%ASSERT(@expected != @actual);
%%ASSERT("Expecting " + @expect + ", but actual value is '" + @actual +
  "'.", @expect, @actual);

%%DEBUGGERPermanent link for this heading

The %%DEBUGGER directive can be used to set a breakpoint in a Fabasoft app.ducx Expression.

Syntax

%%DEBUGGER;

%%LOGPermanent link for this heading

The %%LOG directive can be used to log messages to Fabasoft app.telemetry. app.telemetry provides the log level LOG, IPC, NORMAL, DETAIL, and DEBUG.

Syntax

%%LOG(message);
%%LOG(level, message);

Example

%%LOG("Object created by " + cooobj.objcreatedby.objname); // Detail level
%%LOG("DEBUG", "Object created by " + cooobj.objcreatedby.objname); // Debug level