D Apache FreeMarker Reference

Built-In String FreeMarker Operations

The following table shows you how to use some of the built-in string operations using a string variable called tester as an example. As shown in the following snippet, its value is set to "hello world " (with three trailing blank spaces):
context:
  variables:
    tester: "string"
…
states:
  setVariable:
    component: "System.SetVariable"
    properties:
      variable: "tester"
      value: "hello world   "
  
Note

The following text property definition allows the bot to output either the tester value, or, no string found if no value has been set for the variable.
printVariable:
  component: "System.Output"
  properties:
    text: "${tester.value!'no string found'}"
  transitions:
    ...
Built-In Operation Usage Output
capitalize ${tester.value?capitalize} Hello World
last_index_of ${tester.value?last_index_of('orld')} 7
left_pad ${tester.value?left_pad(3,'_')} ___hello world
length ${tester.value?length} 14
lower_case ${tester.value?lower_case} hello world
upper_case ${tester.value?upper_case} HELLO WORLD
replace ${tester.value?replace('world', 'friends')} hello friends
remove_beginning ${tester.value?remove_beginning('hello')} world
trim ${tester.value?trim} hello world (the trailing three spaces are removed)
ensure_starts_with ${tester.value?ensure_starts_with('brave new ')} brave new hello world
ensure_ends_with ${tester.value?ensure_ends_with(' my friend')}$ hello world my friend
contains ${tester.value?contains('world')?string ('You said world', 'You did not say world')} You said world

The contains('world') expressions returns either true or false. These boolean values are replaced with a string using the string ('string1','string2') function.

ends_with ${tester.value?ends_with('world')?string ('Ends with world', 'Doesn't end with world')} Ends with world
starts_with ${tester.value?starts_with('world')?string ('Starts with world', 'Doesn't start with world')} Doesn't start with world
matches (regular expression returns true or false) ${tester.value?matches('^([^0-9]*)$')} The regular expression returns true or false depending on whether the value contains a number (in which case the boolean value is returned as false). The tester value returns true.
matches (regular expression returns a string) ${tester.value?matches('^([^0-9]*)$')?} Same as above, but this time, true is returned as a string. The matches('regular expression') function returns true or false as boolean types. To print true or false in a System.Output component, use ?string to perform a to-string conversion.

Note: You can’t use regular expression to return a group of values; use them to return a single matching value (or no match).

Example: Improving the Confidence Level with Casing

The casing of the user input can impact the confidence level of the intent resolution. For example, May might refer to the month or the verb. User input can also be erratic (Pizza, piZza, PIZZA). Instead of catching all of the possible case variations as synonyms in the entity definition, you can make the casing uniform using the an FTL operator like lower_case in the following snippet.
getIntent:
    component: "System.Text"
      properties:
        prompt: "Hi, I am a the Pizza Palace bot. How can I help?"
        variable: "userstring"
      transitions:
        next: "toLowercase"
toLowercase:
    component: "System.SetVariable"
      properties:
      variable: "userstring"
      value: "${userstring.value?lower_case}"
    transitions:
      next: "intent"
intent:
     component: "System.Intent"
      properties:
      variable: "iResult"
      sourceVariable: "userstring"
    transitions:
      actions:
      orderPizza: "orderPizza"
      cancelOrder: "cancelOrder"
      unresolvedIntent: "handleUnresolved"

To implement this, you first ask the for the user input. In this example, the System.Text component saves the user input in the userstring variable. The Sytem.SetVariable uses FTL to change the case of the user input string to lower case and saves the modified string to the same userstring variable. Finally, the userstring variable is referenced by the System.Intent component using the sourceVariable property to run the modified user string against the intent engine.

Example: Transforming Case with the System.Switch Component

Another component that can be simplified with FTL is System.Switch.

In the following snippet shows different states that get called depending on the user input (wine or beer), which is stored in the choice variable.
switch:
    component: "System.Switch"
    properties:
      variable: "choice"
      values:
      - "wine"
      - "beer"
    transitions:
      actions:
        wine: "serverWine"
        beer: "serveBeer"
        NONE: "serveWater"
The casing of user input may be inconsistent, even within a word (WiNE). Instead of adding all possible variations to the System.Switch definition, use an FTL operation like upper_case to make the casing uniform:
switch:
  component: "System.Switch"
  properties:
    source: "${choice.value?upper_case}"
    values:
    - "WINE"
    - "BEER"
transitions:
 actions:
  WINE: "serveWine"
  BEER: "serverBeer"
  NONE: "serveWater"

Example: Concatenating FTL Expressions

The following snippet shows how concatenating FTL expressions transforms user input UA1234 and UA 1234, to simply 1234.
normalizeFlightNumber:
  component: "System.SetVariable"
  properties:
    variable: "flight"
    value: "${flight.value?trim?lower_case?remove_beginning('ua ')
            ?remove_beginning('ua')}"

Built-In FreeMarker Number Operations

The following table lists the built-in number operations and shows how they output the value set for the negativeValue (-2.5) and positiveValue (0.5175) context variables in the following snippet.
context:
  variables:
    negativeValue: "float"
    positiveValue: "float"
states:
  setNegativeValue:
    component: "System.SetVariable"
    properties:
       variable: "negativeValue"
       value: -2.5
setPositiveValue:
  component: "System.SetVariable"
  properties:
    variable: "positiveValue"
    value: 0.5175
Operation Example Output
abs ${negativeValue.value?abs} 2.5

The operator turns the negative numeric value into a positive value.

string (used with a numerical value) ${negativeValue.value?abs?string.percent} 250%

The operator first changes the negative value to a positive. Then it converts it into percent, implicitly multiplying the value by 100.

string (with the decimal format value and various currencies)

Tip: Check out Charbase for other currency symbols.

${positiveValue.value?string['###.##']} 0.51
${positiveValue.value?string['###.##%']} 51%

The operator adds adding a percentage character after multiplying the value by 100.

${positiveValue.value?string['##.###\u00A4']} 0.51 $
${positiveValue.value?string['##.###\u20AC']} 0.51 €
${positiveValue.value?string['##.###\u00A3']} 0.51 £
round ${negativeValue.value?round} -2

The operator rounds to the nearest whole number. If the number ends with .5, then it rounds upwards.

${positiveValue.value?round} 1

The operator rounds to the nearest whole number. If the number ends with .5, then it rounds upwards.

floor ${positiveValue.value?floor} 0

The operator rounds downwards.

ceiling ${positiveValue.value?ceiling} 1

The operator rounds upwards.

lower_abc ${negativeValue.value?abs?round?lower_abc} c

The operator turns the negative value into a positive, then rounds it to 3. It returns c, the third letter of the alphabet.

upper_abc ${negativeValue.value?abs?round?upper_abc} C

The operator turns the negative value into a positive, then rounds it to 3. It returns C, the third letter of the alphabet.

is_infinite ${positiveValue.value?is_infinite?string} false

The operator returns false, because a float value is not infinite according to IEEE 754 (Standard for Floating-Point Arithmetic).

Note: The returned value would be a boolean without ?string.

Built-In FreeMarker Array Operations

Array (or sequence) operations enable your bot to, among other things, determine the size of an array, sort arrays, or find content within an array.

You can use arrays to create mock data for testing, or for defining data structures that persist beyond user sessions. You can save an array in a custom component, in a user-scoped variable, or as shown in the following snippet, a context variable. In this example, there are arrays set for the person and colors variables.
context:
  variables:
    person: "string"
    colors: "string"
...

setPerson:
    component: "System.SetVariable"
    properties:
      variable: "person"
      value:
        - firstName: "Frank"
          lastName: "Normal"
        - firstName: "Grant"
          lastName: "Right"
        - firstName: "Geoff"
          lastName: "Power"
        - firstName: "Marcelo"
          lastName: "Jump"

...

setColors:
    component: "System.SetVariable"
    properties:
      variable: "colors"
      value:
        - "yellow"
        - "blue"
        - "red"
        - "black"
        - "white"
        - "green"
These colors and person arrays are used to illustrate the array operations in the following table and in Example: Iterating Arrays.
Operator Example Output
size ${person.value?size?number} 4—The size (four members) of the person array
array index ${person.value[1].firstName} Grant—It’s the value of the second firstName property in the person array.
${person.value[1].firstName !'unknown'} Same as the above, but in this case, the bot outputs unknown if the second firstName property has no value.
first ${person.value?first.firstName} Frank—The first entry of the person array. This operation doesn’t use the array index.
last ${person.value?last.firstName} Marcelo—The final lastName value in the person array.
sort_by ${person.value?sort_by('lastName') [0].firstName} Marcelo
This operator sorts the person array by the lastName property in ascending order. It then prints the value of the corresponding firstName property for final entry in the person array:
  • Jump, Marcelo

  • Normal, Frank

  • Power, Geoff

  • Right, Grant

Note: Unless you save the sorted array in a variable using System.SetVariable, the data remains sorted for a single request only.

${person.value?sort_by('lastName')?reverse[0].firstName} Grant—the values are sorted in descending order:
  • Right, Grant

  • Power, Geoff

  • Normal, Frank

  • Jump, Marcelo

seq_index_of ${colors.value?seq_index_of('red')} 2—The index value for red in the colors array.
seq_last_index_of ${colors.value?seq_last_index_of('red')} 2—The last index value for red in the
join ${colors.value?join(',')} Returns the colors array as a comma-separated string: yellow, blue, red, black, white, green
seq_contains ${colors.value?seq_contains('red')?string('Yes', 'No') Returns Yes because the array contains red.

Note: ?seq_contains returns a boolean value. This value is then replaced by a string using the ?string('…','…') expression.

sort ${colors.value?sort?join(',')} Returns the colors array as a comma-separated string in ascending order: black, blue, green, red, white, yellow
reverse ${colors.value?sort?reverse?join(',')} Returns the colors array as a comma-separated string in descending order: yellow, blue, red, black, white, green

Returning Intents and Scores

You can use array operations to return the results from the intent and entity processing. For example:
  • ${iResult.value.entityMatches[‘name of entity’]} returns an array of entities found in a user string that’s passed to the System.Intent component and stored in the iResult: nlpresult variable.

  • ${iResult.value.intentMatches.summary[n].intent} returns the name of the intent that has a confidence ranking of n, where 0 represents the top-ranked intent, 1 represents the second ranked intent, etc.

  • ${iResult.value.intentMatches.summary[n].score} returns the confidence score for the given intent.
For these two expressions, n is the index of the item you want to look up. For example, the expression to return the top-resolved intent name would be:
${iResult.value.intentMatches.summary[0].intent}
For the top intent's score, the expression would be:
${iResult.value.intentMatches.summary[0].score}

You can use these expressions for intents that scored above the confidence threshold, but you can also use them to return intents whose score falls below the confidence threshold. These expressions are not dependent on the confidence threshold value that's configured in the Skill's Settings page, so you can use them to return the candidate intents and their scores even when no intent could be resolved and an unresolvedIntent action has been triggered. In this case, you can, for example, use these expressions to return the top three intents and their sub-confidence threshold scores.

Note

If you need to refer to the intent that a user has selected after being asked to disambiguate, you can use ${system.intent.name}. (${iResult.value.intentMatches.summary[0].intent} always returns the intent with the top score, which might not be the intent that the user selects when disambiguating.

Example: Iterating Arrays

Arrays determine the number of entities in the user input. The following snippet shows how to determine the size of the array held in the person variable and then iterate over its elements so that the skill outputs something like:

Description of arrayoutput.png follows

  stateName  
    component: "System.CommonResponse"
    properties:
      metadata:
        responseItems:
        - type: "text"
          text: "${person?index+1}. ${person.firstName} ${person.lastName}"
          name: "Sorry"
          separateBubbles: true
          iteratorVariable: "person"
      processUserMessage: false
Note

The output described in this code is not sorted (that is, no sort_by operation is used).

Built-In FreeMarker Date Operations

The following snippet derives the current date using the FreeMarker special variable reference, .now and the built-in date operator.
PrintToday:
  component: "System.Output"
  properties:
   text: "${.now?date}"
   keepTurn: false
The following table lists some of the built-in date operations that you can use to define properties and manipulate entity values.
Operation(s) Example Output
date ${.now?date} The current date
time ${.now?time} The time of day, like 5:46:09 PM
datetime ${.now?datetime} Prints current date and time, like Jan 17, 2018 5:36:13 PM.
long and number_to_date ${(.now?long + 86400000)?number_to_date } Adds 24 hours to the current date. If the call is made on January 17, 2018, FreeMarker outputs January 18, 2018.
string (with formatting styles) ${.now?string.full} Converts the current date to string formatted as Wednesday, January 17, 2018 6:35:12 PM UTC.
${.now?string.long} Converts date to string with the following formatted output: January 17, 20186:36:47 PM UTC.
${.now?string.short} Converts date to string with the following formatted output: 1/17/18 6:37 PM
${.now?string.medium} Converts date to string with the following formatted output: Jan 17, 2018 6:38:35.
${.now?string.iso}

Prints the date in the ISO 8601 standard like 2018-01-17T18:54:01.129Z.

string (with specified output formats) ${.now?string['dd.MM.yyyy, HH:mm']}

Prints the current date in a custom format, like 17.01.2018, 18:58.

${.now?string['yyyy']}

2018

datetime (with string and formatting style) ${date_variable?datetime?string.short} Converts the date to a string formatted as 1/17/18 6:37 PM.

The datetime operator enables FreeMarker to tell if the variable holds a date that contains both date and time information. Similarly, you can use the date or time operators to indicate if the date value contains only the date or only the time, but using datetime?string avoids errors.

Converting the entity value to a string using
  • date

  • long

  • number_to_date

  • formatting styles

  • custom date formats

${dateVar.value.date?long?number_to_date?date?string.short} Converts the date from the entity extraction to a string formatted as 11/17/18.

The date operator tells FreeMarker that the variable only holds a date, not time information. Using this format avoids errors.

${dateVar.value.date?long?number_to_date?string.medium} Converts the date that’s derived from entity extraction to a string formatted as Jan 17, 2018.

Note: All other formats like full, short, long and iso work the same with dates that are derived from entity extraction.

${dateVar.value.date?long?number_to_date?string['dd.MM.yyyy']} Prints the date in custom format. For example: 17.01.2018, 18:58.
${dateVar.value.date?long?number_to_date?string['yyyy']} Prints the date derived from the entity in a custom format.

Example: Extracting Dates from User Input

The following snippet is from a bot that manages appointments. When a user asks it, Can you arrange a meeting with Mr. Higgs a day later than tomorrow?, the bot uses a complex entity, DATE, to extract tomorrow from the request. It outputs the requested date using ${(theDate.value.date?long + 86400000)?number_to_date} to add 24 hours (or 86,400,000 milliseconds) to the current date.
OBotML Code Output
context:
  variables:
    iResult: "nlpresult"
    theDate : "DATE"
states:
   intent:
    component: "System.Intent"
    properties:
      variable: "iResult"
    transitions:
      actions:
        unresolvedIntent: "dunno"
        Appointment: "printToday"
printToday:
  component: "System.Output"
  properties:
    text: "Today is: ${.now}"
    keepTurn: true
startAppointement:
  component: "System.SetVariable"
  properties:
    variable: "theDate"
    value: "${iResult.value.entityMatches['DATE'][0]}"
printDateFound:
  component: "System.Output"
  properties:
    text: "Date found is: ${theDate.value.date}"
    keepTurn: true
printDayAfter:
  component: "System.Output"
  properties:
    text: "A day later is ${(theDate.value.date?long + 86400000)?number_to_date}"
  transistions:
    return: "done"
Outputs the following messages in response to the user message, Can you arrange a meeting with Mr. Higgs a day later than tomorrow?:
  • Today is: 1/18/18 8:31 AM
  • Date found is: Jan 19, 2018
  • A day later is Jan 20, 2018

Example: Setting a Default Date (When No Date Value Is Set)

If the user message doesn’t include any date information, your bot can prompt users for the date, or provide a default date, as shown by the following snippet (which augments the dialog flow in the previous example). To perform the latter, your bot needs to check if the date variable has been set after the NLP engine extracts entities from the user input.
  conditionEquals:
    component: "System.ConditionEquals"
    properties:
      variable: "theDate"
      value: null
    transitions:
      actions:
        equal: "setDefaultDate"
        notequal: "printDateFound"
If no date value has been set, the System.SetVariable component defines a default value in a variable and transform it into a string.
  setDefaultDate:
    component: "System.SetVariable"
    properties:
     variable: "defaultDateInput"
     value: "${.now?datetime?string.long}"
The System.MatchEntity component verifies that this value is a date and then sets the theDATE variable:
  matchEntity:
    component: "System.MatchEntity"
    properties:
      sourceVariable: "defaultDateInput"
      variable: "theDate"
    transitions:
      actions:
        match: "printDateFound"
        nomatch: "exit"
OBotML Output
context:
  variables:
    iResult: "nlpresult"
    theDate : "DATE"
    #need extra variable for default date input
    defaultDateInput: "string"
states:
 ...

#try to extract date information from user sentence
  startAppointement:
    component: "System.SetVariable"
    properties:
      variable: "theDate"
      value: "${iResult.value.entityMatches['DATE'][0]}"
#set default date if none found
  conditionEquals:
    component: "System.ConditionEquals"
    properties:
      variable: "theDate"
      value: null
    transitions:
      actions:
        equal: "setDefaultDate"
        notequal: "printDateFound"
  setDefaultDate:
    component: "System.SetVariable"
    properties:
     variable: "defaultDateInput"
     value: "${.now?datetime?string.long}"
  matchEntity:
    component: "System.MatchEntity"
    properties:
  sourceVariable: "defaultDateInput"
    variable: "theDate"
    transitions:
      actions:
        match: "printDateFound"
        nomatch: "exit"
  printDateFound:
    component: "System.Output"
    properties:
      text: "Date found is:
             ${theDate.value.date?long?number_to_date?date?string.medium}"
      keepTurn: true
  printDayAfter:
    component: "System.Output"
    properties:
    text: "A day later is ${(theDate.value.date?long + 86400000)?number_to_date}"
    transistions:
      return: "done"
The skill outputs the following messages in response to the user message, Can you arrange a meeting with Mr. Higgs a day later than tomorrow?:
  • Today is: 1/18/18 8:31 AM
  • Date found is: Jan 19, 2018
  • A day later is Jan 20, 2018

FreeMarker-Accessible System Variables

Oracle Digital Assistant has a set of system variables through which you can retrieve useful information in your dialog flows through FreeMarker expressions.

In their simplest forms, these expressions take the following form:

${system.variableName}

Some variables can hold objects with nested properties that can be accessed using dot notation after the variable name in the following form.

${system.variableName.propertyName}

In addition, the nested property values can also be objects with nested properties.

Here are the system variables that are available through FreeMarker expressions.

Variable Description
system.actualState Name of the state the user has navigated to by tapping an older "out-of-order" button. If the postback payload contains a system.state property, the dialog engine will navigate to this state and sets this variable to the name of that state. See also Configure the Dialog Flow for Unexpected Actions.
system.authorizedUsers A list of all of the users that have been authorized for a given group chat.
system.channelType The type of channel of the current user session. Allowable values: facebook, androidsdk, iossdk, websdk, slack, twilio, msteams, cortana, webhook, and test .

If the session is running in the tester, the value corresponds to the channel type being simulated.

system.entityToResolve Object representing the current composite bag item to resolve in the System.CommonResponse component when the variable property of the component is referring to a composite bag entity.The object has the following properties:
  • nextRangeStart - index number of the entity's allowable value list that will be navigated to when tapping the Show More button.
  • updatedEntities - list of composite bag items that were updated based on the last user message.
  • needShowMoreButton - Boolean property that can be used in as an expression for the visible property to conditionally render the Show More button to navigate to the next set of entity values.
  • outOfOrderMatches - list of bag items that were populated with a value based on the last user message when the user was prompted for another bag item.
  • rangeStartVar - name of the variable that holds the current range start of the entity values.
  • validationErrors - for the current bag item, list of error messages caused by an invalid value provided in the last user message.
  • allMatches - list of bag items that got a new or updated values based on the last user message.
  • resolvingField - name of current bag item the user is prompted for.
  • userInput - the last user message.
  • skippedItems - list of bag items where the maximum number of prompts for a value is exceeded.
  • disambiguationValues - list of allowed entity values that have matches in the last user message.
  • enumValues - list of entity allowed values for the current bag item.

See The system.entityToResolve Variable for examples of how to use system.entityToResolve.

system.errorAction Error message text of an unexpected error thrown during execution of the state.
system.errorState Name of the state that has thrown an unexpected error during execution.
system.expectedState When user scrolls up the message history, and taps on an older "out-of-order" button this variable is populated with the name of the state that was expected to be executed, but never got executed because the user decided to tap on this "out-of-order" button. See also Configure the Dialog Flow for Unexpected Actions.
system.intent.name Use to refer to the intent that a user has selected after being asked to disambiguate. (
${iResult.value.intentMatches.summary[0].intent}
always returns the intent with the top score, which might not be the intent that the user selects when disambiguating.)
system.invalidUserInput Boolean value set to true when the user input cannot be matched to the requested variable type.
system.message Last message received by Oracle Digital Assistant. This variable has the following properties:
  • channelConversation - the channel conversation object, which has the following properties:
    • botId
    • channelType - When running in the tester, this will return test. If you want to get the name of the channel that is being simulated in the tester, use system.channelType instead.
    • channelName
    • channelCategory
    • groupConversation - Returns true if the conversation is a group chat.
    • userId
    • sessionId
    • sessionExpiryDuration
  • messagePayload - the actual message sent by the user. The properties you can access depend on the type of message:
    • Text message: the text property returning the actual message entered by the user
    • Postback message: the properties of the postback object, typically action and variables when using the System.CommonResponse component. For example, when the user taps a button that sets the variable pizzaSize, this value can be retrieved using following expression:${system.message.messagePayload.variables.pizzaSize}
    • Location message: the location property, which holds a location object with following properties:
      • title
      • url
      • latitude
      • longitude
  • stateCount - the number of states executed to process the last user message.
  • platformVersion - the current runtime platform version.
system.requestedState If a users enters a conversation at a state that requires authorization and the user is not in the list of users stored in system.authorizedUsers, the dialog engine stores the state it intended to execute in this variable.
system.selectedCardIndex This variable holds the index of the selected card when using the facility to optimize card rendering for text-only channels like Twilio. This optimization allows the user to select a card in a two step process: first a list of cards is presented, then the user can enter the number of the cards he wants to select. The corresponding index number of this card is stored in this variable.
Note

The system variables in the above table are the only ones that you can use in FreeMarker expressions. Other system variables are not public and their use is subject to change, which means your skills can't rely on them.

For example, the system.routingFromSkill, system.routingToSkill, system.routingFromIntent, and system.routingToIntent variables are only available for specific digital assistant settings. See System Variables for Digital Assistants.