B 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

While 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 and user input can 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: {}
toLowercase:
    component: "System.SetVariable"
      properties:
      variable: "userstring"
      value: "${userstring.value?lower_case}"
    transitions: {}
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 using the System.Text component. 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 the input collected using the System.Text component may 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.

Array operations 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} returns an array of intents and the confidence level for the given user input.

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 it, 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 and 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')? 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

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 bot outputs something like:

    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"

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"