Chapter 11. Expressions

For many elements (plan bodies, default and initial facts of beliefs, etc.) the developer has to specify expressions in the ADF. The most important part of an expression is the expression string. In addition, some meta information can be attached to expressions, e.g., to specify if the expression should be evaluated once (static) or dynamically for every access (dynamic).

11.1. Expression Syntax

The expression language follows a Java-like syntax. In general, all of the operators of the Java language are supported (with the exception of assignment operators), while no other constructs can be used. Operators are, for example, math operators (+-*/%), logical operators (&&, ||, !), and method, or constructor invocations. Other unsupported constructs are loops, class declarations, variable declarations, if-then-else blocks, etc. As a rule you can use every Java code that can be contained in the right hand side of a variable assignment (e.g., var = <expression>). There are just two exceptions to this rule: Declarations of anonymous inner classes are not supported. Assignment operators (=, +=, *=...) as well as de- and increment operators (++, --) are not allowed, because they would violate declarativeness.

In addition to the Java-like syntax, the language has some extensions: Parameters give access to specific elements depending on the context of the expression. OQL-like select statements allow to create complex queries, e.g., for querying the beliefbase. To simplify the Java statements in the expressions, imports can be declared in the ADF (see Chapter 5, Imports) that allow to use unqualified class names. The imports are defined once, and can be used for all expressions throughout the ADF.

11.2. Expression Properties

Expressions have properties which can be specified as attributes of the enclosing XML tag. The optional class attribute can be specified for any expression, and is used for cross checking the expression string against the expected return type. This allows to detect errors in the ADF already at load time, which could otherwise only be reported at runtime. The evaluation mode influences when and how often the expression is evaluated at runtime. A "static" expression caches the value once the expression created, while the value of a "dynamic" expression is reevaluated for ervery access. The default values of these properties depend on the context in which the expression is used. E.g. initial facts of beliefs are usually static, while conditions are dynamic.

Expressions that are derived from other elements as well as traced conditions (see below) need to know, which changes inside the agent affect the value of the expression. For example, an expression referring to a belief would have to be updated, when the belief has changed. The expression parser is able to autodetect most of these dependencies: References of belief(set)s, to goal parameters, and to the content of the goalbase (e.g., to react to the addition or removal of a goal). But sometimes you may want to react to changes that cannot be detected automatically. The <relevant...> tags (see Figure 11.1, “The Jadex expressions relevant settings XML schema part”) allow to specify an arbitrary number of elements on which an expression depends. These tags require the specification of a reference to an element, i.e., a belief(set), goal, or parameter(set) by using the "ref" attribute. Parameter references take the form "goalname.parametername" unless the goal can be determined from the expression context (e.g., in a goal condition). In this case and for the other references, the name of the element suffices. Optionally, a system event type can be given (see interface jadex.model.ISystemEventTypes for available event types) to further restrict the dependency to only specific changes, such as BSFACT_ADDED using the "eventtype" attribute.

The Jadex expressions relevant settings XML schema part

Figure 11.1. The Jadex expressions relevant settings XML schema part

11.3. Reserved Variables

Within expressions, several variables can be accessed depending on the context the expression is used in. Generally, the following variable names are reserved for agent components and can be accessed directly by their name. In table Table 11.1, “Reserved Expression variables” the reserved variables, their type and accessibility settings are summarized. Values of beliefs and belief sets (from the $beliefbase) and parameter(set)s (from $plan, $event, $goal, and $ref) can be accessed using a shortcut notation (allowing to write statements like "$beliefbase.mybelief"). Note that these variables do not refer to the usual interfaces from jadex.runtime, but to implementation classes from jadex.runtime.impl. In general, you should use these objects as if they were the interfaces.

Table 11.1. Reserved Expression variables

NameClassAccessibiliy
$argsObject[]In any agent expression
$agentRBDIAgentIn any agent expression
$scopeRCapabilityIn any expression
$beliefbaseRBeliefbaseIn any expression
$planbaseRPlanbaseIn any expression
$goalbaseRGoalbaseIn any expression
$eventbaseREventbaseIn any expression
$expressionbaseRExpressionbaseIn any expression
$propertybaseRPropertybaseIn any expression
$goalRGoalIn any goal expression (except creation condition and binding options)
$planRPlanIn any plan expression (except trigger condition and binding options)
$eventREventIn any event expression (except binding otpions)
$refRGoalIn any inhibition arc expression

11.4. Expressions Examples

In Figure 11.2, “Example expressions”, two example expressions are shown. Here the expressions are used to specifiy the facts of some beliefs. In fact there are many places besides beliefs in the ADF where expressions can be used. In the first case, the "starttime" fact expression is evaluated only once when the agent is born. The second belief represents the agent's lifetime and is recalculated on every access.

<belief name="starttime" class="long">
    <fact>
        System.currentTimeMillis()
    </fact>
</belief>
    
<belief name="lifetime" class="long">
    <fact evaluationmode="dynamic">
        System.currentTimeMillis() - $beliefbase.starttime
    </fact>
</belief>

Figure 11.2. Example expressions

11.5. ADF Expressions

The expression language cannot only be used to specify values for beliefs, plans, etc. in the ADF but also for dynamic evaluation, e.g., to perform queries on the state of the agent, most notably the current beliefs. Expressions (jadex.runtime.IExpression) can be created at runtime by providing an expression string. A better way is to predefine expressions in the ADF in the expression base (see Figure 11.3, “The Jadex expressions XML schema part”). Because predefined expressions only have to be parsed and precompiled once and can be reused by different plans, they are more efficient. The following example shows a predefined expression for searching the beliefbase for a certain person contained in the belief persons, using the OQL-like language extension described in more detail below. Moreover, this example uses a custom parameter $surname to specify which person to retrieve from the belief set.

The Jadex expressions XML schema part

Figure 11.3. The Jadex expressions XML schema part

Primary usage of predefined expression is to perform queries, when executing plans. The getExpression(String name) method creates an expression object based on the predefined expression with the given name. In addition, the createExpression(String exp [, String[] paramnames, Class[] paramtypes]) method is used to create an expression directly by parsing the expression string (without referring to a predefined expression). Custom parameters can be optionally be defined for such queries by additionally providing the parameter names and classes. Values for these parameters have to be supplied when executing the query. The expression object provides several execute() methods to evaluate a query specifying either no parameters, a single parameter as name/value pair, or a set of parameters in a jadex.util.Tuple array, where each tuple represents a name/value pair. You can also pre-set parameters before executing the query using the setParameter() method. For example, one can execute the person query with a given surname.

<agent ...>
    ...
    <expressions>
        <expression name="find_person" class="Person">
            select one Person $person
            from $person in $beliefbase.persons
            where $person.getSurname().equals($surname)
            
            <parameter name="$surname" class="String"/>
        </expression>
        ...
    </expressions>
    ...
</agent>

Figure 11.4. Defining an expression in the ADF

public void body {
    IExpression query = getExpression("find_person");
    ...
    Person person = (Person)query.execute("$surname", "Miller");
    ...
}

Figure 11.5. Evaluating an expression from a plan

11.6. OQL-like Select Statements

Jadex provides an OQL-like query syntax, which can be used in conjunction with any other expression statements. OQL (Object-Query-Language) is an extension to SQL (Structured-Query-Language) for object-oriented databases. The generic query syntax as supported by Jadex is very similar to OQL (note that until now only select statements are supported). The syntax is shown in Figure 11.6, “Syntax of OQL-like select statements”.

select (one)? <class>? <result-expression>
from (<class>? $<element> in)? <collection-expression>
    (, <class>? $<element> in <collection-expression>)*
(where <where-expression>)?
(order by <ordering-expression> (asc | desc)? )?

Figure 11.6. Syntax of OQL-like select statements

Unlike OQL and SQL, the keywords (select etc.) are currently case sensitive and have to be written in lower case. Also unlike OQL, the query variable $<element> has to start with the '$' character. The <collection-expression> has to evaluate to an object that can be iterated (an array or an object implementing Iterator, Enumeration, Collection, or Map). In the other expressions (result, where, ordering) the query variables can be accessed using $<element>. When using "$<element>" as result expression, the second "$<element> in" part can be omitted for readability. While you are free to use any expression for the result and the ordering, the where clause, of course, has to evaluate to a boolean value.

Some simple example queries (assuming that the beliefbase contains a belief set "persons", where each person has attributes "forename", "surname", "age", and "address") are shown in Figure 11.7, “Examples of OQL-like select statements”. The first query returns a java.util.List of all persons in the order they are contained in the belief set. The second query only returns persons that are older than 21. In this case a cast is used to invoke the getAge() method. The third example orders the returned list by the addresses of the persons, using a type declaration at the beginning of the query, and therefore does not need a cast for accessing the getAddress() method. The order-by implementation relies on the java.lang.Comparable interface. In the example, the addresses have to be comparable for the query to work. The next query shows that it is possible to use complex expressions to create the result elements. Note, that in this case, the "$person in" part cannot be ommited. The last example shows how to do a join. The expression returns a list of strings of any two (distinct) persons, which have the same address.

select $person from $beliefbase.persons

select $person from $beliefbase.persons where ((Person)$person).getAge()>21

select Person $person from $beliefbase.persons order by $person.getAddress()

select $person.getSurname()+", "+$person.getForename()
from Person $person in $beliefbase.persons

select $p1+", "+$p2 from Person $p1 in $beliefbase.persons,
                         Person $p2 in $beliefbase.persons
where $p1!=$p2 && $p1.getAddress().equals($p2.getAddress())

Figure 11.7. Examples of OQL-like select statements

An extension to OQL is the support of the "one" keyword. The default (without "one") is standard OQL semantics to return all objects matching the query. The "one" keyword is used to select a single element. For queries without ordering, this returns the first found element that matches the query. When using ordering, the query is evaluated for all input elements and returns the first element after having applied the ordering. In both cases null is returned, when no element matches the query. Without the "one" keyword, an empty collection is returned, when no element matches the query.