The expression language provides a convenient way to embed a bit of custom logic in various parts of Sapphire without resorting to Java. Expressions can be used in some of the modeling annotations as well as in UI definitions.
The syntax and semantics borrow extensively from JSP expression language and its precursors, however there are a number of deviations. This document is the definitive reference.
There are three types of expressions: literal expressions, eval expressions
and composite expressions. A literal expression is simply a string such
as Abc
. No quotation is necessary. An eval expression is anything
enclosed between ${
and }
. The syntax and semantics of
eval expressions is discussed in detail in the following sections. A composite
expression is one that combines zero or more literal expressions with one or more eval
expressions.
The following are all examples of valid expressions:
Marry had a little lamb.
${ Name }
${ Name } had a little ${ Animal }.
${ Name == null ? "[contact]" : Name }
${ ( Month == 'February' ? ( LeapYear ? 29 : 28 ) : 30 ) * EmployeeCount }
There are literals for boolean, integer, floating point, string, and null in the expression language.
BooleanLiteral ::= 'true' | 'false'
IntegerLiteral ::= ['0'-'9']+
FloatingPointLiteral ::= (['0'-'9'])+ '.' (['0'-'9'])* Exponent? | '.' (['0'-'9'])+ Exponent? | (['0'-'9'])+ Exponent?
Exponent ::= ['e','E'] (['+','-'])? (['0'-'9'])+
StringLiteral ::= '([^'\]|\'|\\)*' | "([^"\]|\"|\\)*"
"
is escaped as \"
,
'
is escaped as \'
, and \
is escaped as \\
.
Quotes only need to be escaped in a string value enclosed in the same type of quote.NullLiteral ::= 'null'
The expression language follows ECMAScript in unifying the treatment of the .
and []
operators. So expr-a.identifier-b
is equivalent to
expr-a["identifier-b"]
.
Arithmetic operators are provided to act on integer (BigInteger
and
Long
) and floating point (BigDecimal
and Double
)
values.
The evaluation of arithmetic operators is described in the following sections. A and B are the evaluation of subexpressions.
null
, return (Long) 0
.BigDecimal
, coerce both to BigDecimal
and return
A.add( B )
.Float
, Double
, or String
containing
.
, e
, or E
:BigInteger
, coerce both A and B to BigDecimal
and apply operator.Double
and apply operator.BigInteger
, coerce both to BigInteger
and return
A.add( B )
.Long
and apply operator.null
, return (Long) 0
.BigDecimal
, coerce both to BigDecimal
and return
A.subtract( B )
.Float
, Double
, or String
containing
.
, e
, or E
:BigInteger
, coerce both A and B to BigDecimal
and apply operator.Double
and apply operator.BigInteger
, coerce both to BigInteger
and return
A.subtract( B )
.Long
and apply operator.null
, return (Long) 0
.BigDecimal
, coerce both to BigDecimal
and return
A.multiply( B )
.Float
, Double
, or String
containing
.
, e
, or E
:BigInteger
, coerce both A and B to BigDecimal
and apply operator.Double
and apply operator.BigInteger
, coerce both to BigInteger
and return
A.multiply( B )
.Long
and apply operator.null
, return (Long) 0
.BigDecimal
or a BigInteger
, coerce both to
BigDecimal
and return A.divide( B, BigDecimal.ROUND_HALF_UP )
.Double
and apply operator.null
, return (Long) 0
.BigDecimal
, Float
, Double
, or
String
containing .
, e
, or E
,
coerce both A and B to Double
and apply operator.BigInteger
, coerce both to BigInteger
and
return A.remainder( B )
.Long
and apply operator.null
, return (Long) 0
.BigDecimal
or BigInteger
, return A.negate()
.String
:.
, e
, or E
, coerce to a
Double
and apply operator.Long
and apply operator.Byte
, Short
, Integer
, Long
,
Float
, or Double
, retain type and apply operator.Relational operators are provided to compare two values.
The evaluation of relational operators is described in the following sections. A and B are the evaluation of subexpressions.
==
B, return true
.null
or B is null
return false
.BigDecimal
, coerce both A and B to BigDecimal
and return A.equals( B )
.Float
or Double
coerce both A and B to
Double
, apply operator.BigInteger
, coerce both A and B to BigInteger
and return A.equals( B )
.Byte
, Short
, Character
,
Integer
, or Long
coerce both A and B to Long
,
apply operator.Boolean
coerce both A and B to Boolean
,
apply operator.String
coerce both A and B to String
, compare
lexically.A.equals( B )
.==
B, return false
.null
or B is null
return true
.BigDecimal
, coerce both A and B to BigDecimal
and return ! A.equals( B )
.Float
or Double
coerce both A and B to
Double
, apply operator.BigInteger
, coerce both A and B to BigInteger
and return ! A.equals( B )
.Byte
, Short
, Character
,
Integer
, or Long
coerce both A and B to Long
,
apply operator.Boolean
coerce both A and B to Boolean
,
apply operator.String
coerce both A and B to String
, compare
lexically.! A.equals( B )
.null
or B is null
, return false
.BigDecimal
, coerce both A and B to BigDecimal
and use the return value of A.compareTo( B )
.Float
or Double
coerce both A and B to
Double
apply operator.BigInteger
, coerce both A and B to BigInteger
and use the return value of A.compareTo( B )
.Byte
, Short
, Character
,
Integer
, or Long
coerce both A and B to Long
and apply operator.String
coerce both A and B to String
, compare
lexically.Comparable
, then use result of A.compareTo( B )
.B.compareTo( A )
.==
B, return true
.null
or B is null
, return false
.BigDecimal
, coerce both A and B to BigDecimal
and use the return value of A.compareTo( B )
.Float
or Double
coerce both A and B to
Double
apply operator.BigInteger
, coerce both A and B to BigInteger
and use the return value of A.compareTo( B )
.Byte
, Short
, Character
,
Integer
, or Long
coerce both A and B to Long
and apply operator.String
coerce both A and B to String
, compare
lexically.Comparable
, then use result of A.compareTo( B )
.B.compareTo( A )
.null
or B is null
, return false
.BigDecimal
, coerce both A and B to BigDecimal
and use the return value of A.compareTo( B )
.Float
or Double
coerce both A and B to
Double
apply operator.BigInteger
, coerce both A and B to BigInteger
and use the return value of A.compareTo( B )
.Byte
, Short
, Character
,
Integer
, or Long
coerce both A and B to Long
and apply operator.String
coerce both A and B to String
, compare
lexically.Comparable
, then use result of A.compareTo( B )
.B.compareTo( A )
.==
B, return true
.null
or B is null
, return false
.BigDecimal
, coerce both A and B to BigDecimal
and use the return value of A.compareTo( B )
.Float
or Double
coerce both A and B to
Double
apply operator.BigInteger
, coerce both A and B to BigInteger
and use the return value of A.compareTo( B )
.Byte
, Short
, Character
,
Integer
, or Long
coerce both A and B to Long
and apply operator.String
coerce both A and B to String
, compare
lexically.Comparable
, then use result of A.compareTo( B )
.B.compareTo( A )
.null
or B is null
return false
.true
if A == B[ i ]
for any value of i
between 0
and B.Size - 1
.
Logical operators are provided to perform boolean logic.
The evaluation of logical operators is described in the following sections. A and B are the evaluation of subexpressions.
Boolean
, apply operator.Boolean
, apply operator.Boolean
, apply operator.The empty operator is a prefix operator that can be used to determine if a value is
null
or empty.
null
, return true
.String
, then return true
.true
.Collection
, return true
.false
.Return B or C, depending on the result of the evaluation of A.
Boolean
:true
, return B.false
, return C.Parentheses can be used to change precedence, as in: ${(a*(b+c))}
.
Highest to lowest, left to right.
[] .
()
- (unary) not ! empty
* / div % mod
+ - (binary)
< > <= >= lt gt le ge
== != eq ne in
&& and
|| or
? :
Qualified functions with a namespace prefix have precedence over the operators.
Thus the expression ${c?b:f()}
is illegal because b:f()
is being parsed as a qualified function instead of part of a conditional expression.
As usual, ()
can be used to make the precedence explicit,
e.g ${c?b:(f())}
.
The expression language supports functions, whose names can be qualified with a namespace.
The full syntax of qualified n-ary function is as follows:
[ns:]f([a1[,a2[,...[,an]]]])
Where ns
is the namespace prefix, f
is the name of the
function, and a
is an argument.
The following functions are available for use:
Name | Description |
---|---|
Absolute | Returns the absolute path of a value for properties with a RelativePathService. |
Avg | Computes the average of numbers in a collection. Typically, this function takes the collection as the sole parameter. However, when the collection is a model element list, a second parameter may be necessary to specify the name (in the form of a string) of the list entry's value property to use in aggregation. If the the collection is a model element list and the second parameter is not specified, this function will use list entry's first value property for aggregation. An average of an empty collection is 0. |
Concat | Concatenates two or more strings into a single string. Particularly useful in contexts where composite expressions cannot be used, such as where the result of concatenation needs to feed into another function or operator. |
Content | Returns the content of a value or a transient. For value properties, the default is taken into account, if applicable. |
Enabled | Returns the enablement of a property. |
Enabled | Determines if the property associated with the property editor is enabled. Can only be used in the context of a property editor. |
EncodeToXml | Encodes text for use as XML element content or an attribute value. |
EndsWith | Tests if a string ends with the specified suffix. |
FirstSegment | Breaks a string into segments using the provided separators and returns the first segment. If no separators are found in the string, the entire string is returned. This function takes two string operands. The first is the string to tokenize. The second is a sequence of characters that individually should be treated as valid separators. |
Fragment | Returns a fragment of a string. The fragment starts at the index specified by the second argument and extends to the character before the index specified by the third argument. The length of the fragment is end index minus start index. A negative start index is normalized to zero. A start index exceeding the length of the input is normalized to the length of the input. An end index exceeding the length of the input is normalized to the length of the input. An end index that is smaller than the start index is normalized to the start index. |
Global | Returns a map of global objects maintained through Sapphire.global() API. |
Head | Returns a fragment of a string starting at the beginning and not exceeding the specified length. A negative fragment length is normalized to zero. A fragment length exceeding the length of the input is normalized to the length of the input. |
Image | Returns the image associated with the context model element. |
Index | Determines the index of a model element within its parent list. |
InstanceOf | Determines if an object is of specified type. The object to be checked is the first operand and the type is the second operand. The type must be a fully-qualified Java class name. |
LastSegment | Breaks a string into segments using the provided separators and returns the last segment. If no separators are found in the string, the entire string is returned. This function takes two string operands. The first is the string to tokenize. The second is a sequence of characters that individually should be treated as valid separators. |
List | Constructs a list from arbitrary number of operands. |
LowerCase | Converts a string to lower case. Can be used either as LowerCase( Name ) or as Name.LowerCase(). |
Matches | Determines whether a string matches a regular expression. The full semantics are specified by Java's String.matches() function. |
Max | Finds the largest number in a collection. Typically, this function takes the collection as the sole parameter. However, when the collection is a model element list, a second parameter may be necessary to specify the name (in the form of a string) of the list entry's value property to use in aggregation. If the the collection is a model element list and the second parameter is not specified, this function will use list entry's first value property for aggregation. |
Message | Returns the message from a validation result. |
Min | Finds the smallest number in a collection. Typically, this function takes the collection as the sole parameter. However, when the collection is a model element list, a second parameter may be necessary to specify the name (in the form of a string) of the list entry's value property to use in aggregation. If the the collection is a model element list and the second parameter is not specified, this function will use list entry's first value property for aggregation. |
Parent | Returns the parent of the current model element. |
Parent | Returns the parent of the given part. |
Part | Returns the context part. |
Replace | Replaces all occurrences of a regular expression pattern with the provided replacement text. The full semantics are specified by Java's String.replaceAll( String, String ) function. |
Root | Returns the root of the model. |
SapphireVersionMatches | Determines whether Sapphire version matches a version constraint. A version constraint is a comma-separated list of specific versions, closed ranges (expressed using "[1.2.3-4.5)" syntax and open ranges (expressed using "[1.2.3" or "4.5)" syntax). The square brackets indicate that the range includes the specified version. The parenthesis indicate that the range goes up to, but does not actually include the specified version. |
Scale | Changes the scale of a decimal. |
Severity | Returns the severity of a validation result. |
Size | Determines the size of a collection, a map, an array or a string. |
StartsWith | Tests if a string starts with the specified prefix. |
State | Returns the root element of editor page's persisted state, allowing access to various state properties. This is particularly useful when the persisted state is extended with custom properties wired to custom actions, as it allows any EL-enabled facility to integrate with the custom state property. |
Sum | Computes the sum of numbers in a collection. Typically, this function takes the collection as the sole parameter. However, when the collection is a model element list, a second parameter may be necessary to specify the name (in the form of a string) of the list entry's value property to use in aggregation. If the the collection is a model element list and the second parameter is not specified, this function will use list entry's first value property for aggregation. A sum of an empty collection is 0. |
Tail | Returns a fragment of a string starting at the end and not exceeding the specified length. A negative fragment length is normalized to zero. A fragment length exceeding the length of the input is normalized to the length of the input. |
Text | Returns the text of a value, taking into account the default, if applicable. |
UpperCase | Converts a string to upper case. Can be used either as UpperCase( Name ) or as Name.UpperCase(). |
Validation | Returns the validation result of a property. |
Validation | Returns the validation result of a part. |
VersionMatches | Determines whether a version matches a version constraint. A version constraint is a comma-separated list of specific versions, closed ranges (expressed using "[1.2.3-4.5)" syntax and open ranges (expressed using "[1.2.3" or "4.5)" syntax). The square brackets indicate that the range includes the specified version. The parenthesis indicate that the range goes up to, but does not actually include the specified version. |
Custom functions can be contributed via Sapphire Extension System.
The expression language supports Java enumerations. Coercion rules for dealing
with enumerated types are included in the following section. Also, when referring
to values that are instances of an enumerated type from within an expression, use
the literal string value to cause coercion to happen via the below rules. For example,
Let's say we have an enum called Suit that has members Heart, Diamond, Club, and
Spade. Furthermore, let's say we have a reference in the expression, mySuit, that is a Spade.
If you want to test for equality with the Spade enum, you would say
${mySuit == 'Spade'}
. The type of the mySuit will trigger the invocation of
Enum.valueOf( Suit.class, "Spade" )
.
Every expression is evaluated in the context of an expected type. The result of the expression evaluation may not match the expected type exactly, so the rules described in the following sections are applied.
String
, return A.null
, return ""
.Enum
, return A.name()
.array
, List
or Set
, return concatenation of
items separated by a comma (,) character.A.toString()
.null
or ""
, return 0
.Character
, convert A to new Short( (short) a.charValue() )
,
and apply the following rules.Boolean
, then error.Number
type N, return A.Number
, coerce quietly to type N using the following algorithm:BigInteger
:BigDecimal
, return A.toBigInteger()
.BigInteger.valueOf( A.longValue() )
.BigDecimal
:BigInteger
, return new BigDecimal( A )
.new BigDecimal( A.doubleValue() )
.Byte
, return new Byte( A.byteValue() )
.Short
, return new Short( A.shortValue() )
.Integer
, return new Integer( A.intValue() )
.Long
, return new Long( A.longValue() )
.Float
, return new Float( A.floatValue() )
.Double
, return new Double( A.doubleValue() )
.String
, then:BigDecimal
then return new BigDecimal( A )
.BigInteger
then return new BigInteger( A )
.N.valueOf( A )
.null
or ""
, return (char) 0
.Character
, return A.Number
, coerce quietly to type Short
, then return
a Character
whose numeric value is equivalent to that of a Short
.String
, return A.charAt( 0 )
.null
or ""
, return false
.Boolean
, return A.String
, return Boolean.valueOf( A )
.null
, return null
.""
, return null
.String
, return Enum.valueOf( T.getClass(), A )
.null
, return null
.List
, return A.Collection
, return list of collection items.array
, return list of array items.String
, then:""
, return empty list.null
, return null
.""
, return null
.The subject of string concatenation in an expression can be a source of confusion. In particular,
expressions like ${ "A" + "B" }
cause an error during evaluation. That's
because the +
operator is exclusively arithmetic
addition. It is not overloaded to also mean string concatenation, like it is in
some other languages such as Java.
String concatenation in an expression can be accomplished in one of two ways:
On the year ${ Year % 100 } of ${ Year / 100 } century.
${ OnlyShowYear ? Year : concat( Year, "-", Month, "-" Day ) }
Using the concat
function is particularly necessary in cases where
the result of concatenation should feed into another function or operator.