Reference‎ > ‎

The expression language

ABL uses an expression language in a few places, such as Constraint and Formula annotations.

These expressions are interpreted using Apache's excellent Jexl engine (description of the language here).

Annotation expressions

Expressions can be used for Constraint (and its close cousin, CommitConstraint) and for Formulas. In these annotations, you can access the properties of the @CurrentObject directly. For instance, if you have a constraint defined on a Customer object, you could write:

@Constraint("balance < company.creditLimit")

In this case, balance refers to the Customer's balance attribute, and company.creditLimit refers to the creditLimit attribute of the Company related to the Customer through its company relationship.

Note that you can refer to the @CurrentObject's attributes, and to attributes of its parent objects, but the buck stops there. You cannot refer to its grandparent objects, nor can you refer to any child objects.

A few things to keep in mind:

Most null attributes are converted for convenience

  • Null numeric attributes are converted to zero
  • Null boolean attributes are converted to false
  • Null string and character attributes are converted to empty string
The purpose of this conversion is so that you can keep expressions simple, such as:

@Constraint("num1 = 0 and num2 = 0")

instead of the much more ponderous:

@Constraint("(num1 = null or num1 = 0) and (num2 = null or num2 = 0)")

In formulas (but not aggregates or constraints), null relationships will also result in this conversion. For instance, if a formula is defined as:

@Formula("customer.balance")

and the customer property is null, the value of the formula will be zero (assuming balance is a numeric attribute, of course).

We fully realize that these conversions are shortcuts. If this treatment of nulls is not what you need, you can easily code it in Java or Groovy:

@Constraint public void myConstraint() {
    if (bean.num1 == null)
        // Do something
    else if (bean.num1 == 0)
        // Do something else
}

Note that null date attributes are not converted.

There is no access to old values

If you need access to old values, you will need to implement it in code.

Integer arithmetic can result in unexpected rounding

Assume you have two attributes that are counts (and therefore, typically, integers) and you'd like to divide one by the other (for instance, to compute a percentage). You might be tempted to do this with an expression such as:

@Formula("(numGoodClients / numClients) * 100")

This would not work properly because numGoodClients and numClients are both integers, and therefore the division results in an integer. If numGoodClients is 1, and numClients is 2, the result of the division will be zero. To avoid this "feature", use the following expression instead:

@Formula("(numGoodClients * 100) / numClients")

Of course, this assumes that numClients will never be zero...

Error message expressions

In Constraint annotations, the errorMessage parameter can have embedded expressions that refer to the @CurrentObject's properties. For instance:

@Constraint(value="balance < company.creditLimit",
    errorMessage="Customer {name} has a credit limit {creditLimit} which exceeds company {company.name}'s credit limit")
public void myConstraint() { }

You have full access to Jexl's power in these expressions. There are no limitations.

Comments