10. ExpressionParser

10.1. Introduction

The ExpressionParser is the part of Gentics Portal.Node that parses and interprets expressions, like rules (prules), datasource filters and assignment commands in reactions and the GeneralViewAction. See Section 8.2.13, “ExpressionParser Configuration” for detailed information about configuring the ExpressionParser and compatibility issues with older implementations.

10.2. Syntax

10.2.1. Introduction

This section describes the valid syntax for expressions. In general, there are two different types of expressions: assignments and rules.

10.2.2. Assignments

Assignments are of the form [Name] [Assignment Operator] [Expression].

Examples for valid assigments are:

Example 4.47. Examples for valid assignments

view.components.firstname.data = "Franz"
portal.properties.values += ["one", "two", "three"]
portal.properties.values -= data.object.unwanted
data.object.number = 18 + (23 - portal.vars.timestamp * 42)
                    

10.2.3. Assignment Operators

Assignment operators are used in Assignments and define how the lefthand-side property is to be modified.

Table 4.161. Assignment Operators

OperatorDescription
=Assign the right hand side value(s) to the left hand side property.
+=Add the right hand side value(s) to the multivalue left hand side property.
-=Remove the right hand side value(s) from the multivalue left hand side property.

10.2.4. Expressions

An Expression is any legal combination of Operations, Constants, Literals, Names and Functions. Expressions may NOT contain Assignment Operators.

10.2.5. Rules

Rules are basically boolean Expressions (Expressions that evaluate to true or false) and can be used as rules for boolean settings or datasource filters.

10.2.6. Binary Operations

Binary Operations define how operators (left hand side [lhs] operator and righ hand side [rhs] operator) shall be combined to calculate other values. The supported values of the operators and the generated value depend on the Operator and are listed below.

Table 4.162. Binary Operations

OperatorSupported operator typesResult typeDescription
ORbooleanbooleanLogical or: is true when at least one of the operands is true. Can also be written as ||.
ANDbooleanbooleanLogical and: is true when both operands are true. Can also be written as &&.
==anybooleanEquality comparison: is true when the operand values are equal.
!=anybooleanUnequality comparison: is true when the operand values are not equal.
>numbersboolean"Greater" comparison: is true when the numerical value of the lhs operator is greater than the numerical value of the rhs operator.
>=numbersboolean"Greater or equal" comparison: is true when the numerical value of the lhs operator is greater or equal than the numerical value of the rhs operator.
<numbersboolean"Smaller" comparison: is true when the numerical value of the lhs operator is smaller than the numerical value of the rhs operator.
<=numbersboolean"Smaller or equal" comparison: is true when the numerical value of the lhs operator is smaller or equal than the numerical value of the rhs operator.
LIKEstringsboolean"Like" operation: the result is true, when the rhs value matches the pattern of the lhs value. The lhs value might contain the character % as wildcard for any character sequence.
CONTAINSONEOFarraysboolean"Contains one of": the result is true, when the lhs values contain at least one of the values of the rhs array.
CONTAINSNONEarraysboolean"Contains none of": the result is true, when the lhs values contain none of the values of the rhs array.
CONTAINSALLarraysboolean"Contains all": the result is true, when the lhs values contain all of the values of the rhs array. Note: currently, this operator can only be used with static values (i.e. it is not possible to filter a datasource with a rule like "object.attribute CONTAINSALL ['a', 'b', 'c']")
+numbersnumber"Summation" operation: the result is the sum of the operands values.
-numbersnumber"Subtraction" operation: the result is the difference of the operands values.
*numbersnumber"Multiplication" operation: the result is the product of the operands values.
/numbersnumber"Division" operation: the result is the quotient of the operands values. This will fail when the rhs value is 0.
%numbersnumber"Modulus" operation: the result is the modulus of the integer division of the operands. This will fail when the rhs value is 0.

10.2.7. Unary Operators

Unary Operations modify values of a single operand.

Table 4.163. Unary Operations

OperatorSupported operator typeResult typeDescription
+numbernumberThis operation does not modify the value (is just for the sake of completeness).
-numbernumberModifies the sign of the numerival value.
!booleanbooleanLogical "Not" operation: Switches true and false.

10.2.8. Constants

There are three constants: true, false and null (empty value).

10.2.9. Literals

There exist four types of literals: integer numbers, floating point numbers, strings and arrays.

Table 4.164. Literals

TypeDescription
IntegerInteger literals can be notated in decimal form (starting with a number, but not with 0), in hexadecimal notation (starting with 0x) or in octal notation (starting with 0).
Floating Point NumberFloating point numbers can be given in the technical notation, including signs and exponents (see examples below).
StringStrings have to be enclosed by " (double quotes) or ' (single quotes). The special characters enclosing character (double or single quote) and backslash (/) have to be escaped by backslash. Newline is written as \n and tabulator as \t.
ArrayArrays are notated as [value1, value2, ...], where the values may be literals (inluding nested arrays) or constants.

Example 4.48. Examples of literals in expressions

42 (decimal integer)
-99 (signed decimal integer)
0xff (hexadecimal integer, decimal value: 255)
010 (octal integer, decimal value: 8)
-18.98e+1 (floating point integer, value: -189.8)
31.415926e-1 (floating point integer, value: 3.1415926)
"Franz" (string literal)
'Sepp' (string literal)
"\"'\n\t" (string literal, containing escaped characters)
["Franz", 42, true] (array)
[['Sepp', 1.95], ['Franz', 1.84]] (nested arrays)
                    
[Warning]Warning
There is an incompatibility between the old RuleParser and the ExpressionParser: The ExpressionParser is more strict in the notation of string literals, they always have to be enclosed by double or single quotes, whereas the RuleParser also interprets names that did not contain a . (dot) as strings.

Example 4.49. Expressions that are interpreted differently by RuleParser and ExpressionParser

portal.user.roles CONTAINSONEOF admin

was a legal rule for the old RuleParser, but would be interpreted differently in the ExpressionParser ( admin will be interpreted as name and will most likely resolve to null ). This rule should be rewritten into

portal.user.roles CONTAINSONEOF "admin"

or - more correctly -

portal.user.roles CONTAINSONEOF ["admin"]

10.2.10. Names and Variables

All character sequences that are not operators, literals, constants or functions (see below) and for which the rules for Java identifiers apply, are interpreted as names. Sequences of names that are only separated by . (dots) are interpreted as name-paths. When expression containing names are evaluated, name-paths are resolved into their current values (which might be null). Special name-paths starting with object. are interpreted as variables. When the expression is used as filter for datasource queries, the variables are placeholders for attributes of the filtered objects and their properties. For details on Java identifiers, see the Javadoc for Character.isJavaIdentifierPart() .

Example 4.50. Datasource filter with variable

The datasource filter

object.name LIKE "N*"

would filter all objects, that have an attribute name with a value starting with N .

10.2.11. Functions

Functions are Names followed by parenthesis () . Functions might have function parameters that can be Literals, Constants, Name-paths, Functions or even Expressions.

Table 4.165. ExpressionParser functions

NameParametersResult valueDescription
concat(string, string, ...) 2 or morestringConcatenates the given strings.
isempty(object) 1boolean Returns true when the object's value is null, an empty string or an empty Collection or Array.
subrule(attribute, subrule) 2array This function can only be used in datasource filters for datasources of type contentrepository. It generates a subquery and returns the given attribute from all filtered objects. Note that for compatibility reasons, the subrule must contain variables as subobject instead of object .
if(expression1, expression2[, expression3]) 2 or 3any

Evaluates expression2 when expression1 is true or expression3 when expression1 is false and 3 parameters where given.

do(assignment1[, assignment2[, assignment3]...]) 1 or moreassignment

Performs all given assignments in the given order. This function can be nested within the if() function to perform a sequence of assignments, when the given expression evaluates to true .

fromArray(array, index) 2any

Fetches the object with index index from the given array .

matches(object, expression) 2boolean

The matches() function implements a special kind of permission rule (for Gentics .Node ContentRepository datasource filters and evaluation). The function is used like:

matches(portal.user.permission, object.location CONTAINSONEOF this.location && object.region CONTAINSONEOF this.region)

Meaning: filter all objects that matches at least one of the given user permissions (portal.user.permission). portal.user.permission is supposed to be a collection of objects with attributes location and region each. An object matches the user's permissions if it matches the given rule agains one of those permission objects that are referred to as this in the rule. This is different from the rule

object.location CONTAINSONEOF portal.user.permission.location && object.region CONTAINSONEOF portal.user.permission.region

because in this rule, it is not garantueed that the filtered object matches location and region for the SAME permissions object.

eval(expression) 1any The eval() function takes the given expression, evaluates it and then interprets the result as part of the outer expression. This can for example be used for expressions that are configured in properties: The expression eval(portal.properties.rule) would first resolve the property portal.properties.rule (e.g. to portal.user.isloggedin ) and then evaluate this as rule.
insert(array, newobject[, index[, unique]]) 2 - 4array The insert() function can be used to insert an object to a given array (or collection) at the specified position. The optional parameter index defines the position where to insert the newobject into the given array , where 0 would be the start and -1 the end (which is the default). When the parameter unique is set to true (default is false ), the inserted object is removed from the array first (if contained), thus ensuring the uniqueness of the object in the array. It is important to notice, that this function does not modify the array itself, but merely returns a copy with the requested modifications done.
get(object, attributeName) 2any The get method allows you to get a given attributeName from the object. This can be used, when the attributeName itself is not static, but is itself resolved or constructed by using functions.
set(object, attributeName, value) 3noneThe set method allows you to set a given attributeName to a defined value for the given object.
foreach(Map|Collection|array, iteratorName, loopBody) 3list

Allows you to iterate over a Map, Collection or array which is stored in a property named as 'iteratorName' within the 'loopBody'. (loopBody can be any other function call, e.g. do()). This function returns a list of all loopBody results.

foreach(portal.pages, "page", 
page.reset = true)

echo(Object) 1NoneSimilar to the EchoAction the echo function is only suitable for debugging - it will force a info log output containing string representation of the passed in value.
i18n(string [,string]) 1-2stringTranslate the first parameter using the dictionary. The second parameter is optional and specifies the language id. If not specified the current language will be used.
docallableaction("module Id", "view Id", "callable action id") 3BooleanInvokes the callable action with the given id which is defined in the given module id and view id. Returns true if action sequence was invoked successfully. See also Section 2.2.9, “Callable Actions”
getContentID(datasource, pub_dir, filename, [node_id]) 3-4StringTries to convert the given input data (pub_dir, filename, optionally node_id) into the contentid by looking in the configured datasource. See also Section 4, “GenticsContentModule” for details on referencing content by pub_dir, filename and node_id.
filter(rule, "postprocessorclass"[, data]) 2-3RuleThe filter() function can only be used in datasource filters. When used it will create an instance of the class postprocessorclass and call the process() method with the objects that were returned by the rule.
[Tip]Tip

The if() function can be used for conditional assignments, since expression2 and expression3 may also be assignments!

Example 4.51. Example for conditional assignments

if(portal.user.isloggedin, data.value = "yes", data.value = "no")
  or
data.value = if(portal.user.isloggedin, "yes", "no")
                        
[Warning]Warning

The if() function can not be used in datasource filters.

[Warning]Warning

The eval() function can only be used in a limited way in datasource filters: currently the result of the inner expression will be evaluated first and then inserted into the outer expression. Therefore the result of the eval() function must not contain something like object. .

[Tip]Tip

The filter() function can be used to replace filtering rules, which would result in complex SQL statements (like e.g. filtering for multivalue attributes) with a Java implementation.

Example 4.52. Example for using the filter() function

Let's assume the following rule to get pages from a folder that match the user's permissions groups:

object.obj_type == 10007 AND object.folder_id = data.folder_id
  AND object.permgroups_view CONTAINSONEOF portal.user.groups
                	

Since the attribute permgroups_view is a multivalue attribute (and therefore not optimizable), the resulting SQL statement will contain at least one join.

By moving the logic of filtering by permission groups into an instance of PostProcessor and using the filter() function, the complex SQL statement can be avoided.

Example implementation of the PostProcessor

package com.example;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.gentics.api.lib.etc.ObjectTransformer;
import com.gentics.api.lib.expressionparser.EvaluationException;
import com.gentics.api.lib.expressionparser.ExpressionEvaluator;
import com.gentics.api.lib.expressionparser.filtergenerator.PostProcessor;
import com.gentics.api.lib.resolving.Resolvable;

/**
 * Permission filter
 */
public class PermissionFilter implements PostProcessor {
  /**
   * Process the objects
   * @param resolvables collection of resolvables
   * @param data data object (containing the user permissions)
   */
  public void process(List<Resolvable> resolvables, Object data)
      throws EvaluationException {
    // get the user permissions, which are provided to the filter() function
    Collection userPerms = ObjectTransformer.getCollection(data, Collections.EMPTY_LIST);

    // iterate over all resolvables
    for (Iterator<Resolvable> i = resolvables.iterator(); i.hasNext();) {
      Resolvable res = i.next();
      // get the object permissions
      Collection objPerms = ObjectTransformer.getCollection(
        res.get("permgroups_view"), Collections.EMPTY_LIST);

      // filter out the objects with non-matching permissions
      if (!ExpressionEvaluator.containsOneOf(objPerms, userPerms)) {
        i.remove();
      }
    }
  }
}
                	

Example usage

filter(object.obj_type == 10007 AND object.folder_id = data.folder_id,
 'com.example.PermissionFilter', portal.user.groups)
                	

10.2.12. Evaluation priority

The evaluation priority of different expression parts can be seen in the following table (operations with highest priority listed on top):

  1. Constants, Literals, Names and Functions

  2. Unary operations

  3. Multiplicative calculation operations

  4. Additive calculation operations

  5. Comparison operations

  6. Boolean operations

  7. Assignments

Sequences of operations with the same priority level are evaluated from right to left. It is legal to use parenthesis within expressions to modify the evaluation order.