Vorro Health Logo

RelateScript

This manual documents the BlueStep Relate formula language called RelateScript. This documentation is written for experienced computer users who are not necessarily software engineers, but who can write a basic spreadsheet formula or can hand-edit some HTML. This documentation is a work in progress, as is the language. Both are upgraded on a regular basis, so check back often to see if the features you want have been added.

Contact Us »

This section is currently under construction. Follow the link below to Relate Script on the previous ClientCare site.

Relate Script »

Expand/Collapse All »


How and Where are Formulas Used

Return to top

Formulas are currently available in four places: 1) on most Relate form fields, 2) in Merge Reports, 3) as stand-alone formulas and 4) as end-points. Stand-alone formulas, themselves, have six varieties: scheduled, pre-edit, pre-save, post-save, pre-delete and on-demand. This makes a total of nine types of formulas. Their differences are explained briefly in this article and also in the last section of this document in the article titled, "Types of Formulas." Other detailed articles explaining how formulas are used are "The 8 Step Edit Process", "Writable or Read-Only?", "Accessing Field Data" and "Field Formulas"

Field Formulas

The first type, field formulas, are executed when the record is saved and these formulas will both read and update fields. Field formulas are limited to the form on which they occur and will not read or update fields on other forms.

Merge Report Formulas

Merge Report formulas have access to all of the data in a single Relate record, no matter what form(s) contain the data. They can also access data in any other Relate records. These formulas can read data and produce values for display, but cannot update the data. The formula is run each time the report is viewed.

Scheduled Formulas

Scheduled formulas run on a schedule and, therefore, do not require any user action to do their work. As a matter of fact, they are frequently used to send notification emails when a user fails to take a required action such as to filing a weekly report or renewing an annual certification. They are also useful in keeping information that naturally changes over time fresh, such as counting the days elapsed since a prior event. They are also used to prepare data for editing which must be done on a regular basis. Scheduled formulas can access and modify any and all data in Relate. However, they do require a current record be designated. For each scheduled time, they run once for each record that matches specific type, category and unit criteria. Each matching record in turn becomes the current record for the formula. Scheduled formulas also have control over database transactions which allows data to be stored in multiple distinct steps.

Pre-edit Formulas

Pre-edit formulas are executed before the user edits data on a particular form. They have access to all other data in the current record, but can only modify the data that is about to be edited. They can also access data in any other Relate records. The results of this type of formula are not actually saved, but simply used to pre-populate data for the user to possibly modify, then save. They are primarily used to dynamically pre-populate default values using more complex logic than is available in the basic default-value field settings.

Pre-save Formulas

Pre-save formulas are executed immediately after field formulas and serve a similar purpose. The major difference is that Pre-save Formulas have access to read and update information contained outside the form entry currently being edited. They can read and modify any information in the current record as well as all other Relate records.

Post-save Formulas

Post-save formulas can do almost exactly the same things as pre-save formulas. However, they run after the data has already been saved. Therefore, any modifications made by this type of formula will be recorded as a separate save. These formulas benefit from being able to work with information that is not available until after the save is complete, such as the results of other types of formulas. They can also get the URL to view or edit newly created data which did not have an identifier assigned, and therefore no URL, prior to it being saved for the first time. For this reason, post-save formulas are most often used to send notification of new or modified data via an email, intramail or alert containing a link back to the new information.

Pre-Delete Formulas

Pre-delete formulas run prior to entries being deleted. However, any relationship fields will have been cleared of all selected values in preparation for delete. Pre-delete formulas are used primarily for two purposes. The first is to prevent deletion of important data. The pre-delete formula can roll-back the current operation and prevent the delete from occurring. The second primary use is to clean up dependant data. For instance, when deleting a timecard entry the overtime hours need to be re-calculated for any remaining timecards later in the same week.

On-Demand Formulas

On-Demaind formulas run on a dynamic schedule specified in another formula. They cannot be run directly by any user action. They are used to run large computations out-of-process or run a formula on a delay after another formula runs. On-demand formulas have a primary form entry or primary record, and an optional message from the formula initiating the on-demand formula. If used carefully they may increase both actual performance and perceived performance. On-demand formulas also have control over database transactions which allows data to be stored in multiple distinct steps.

End-Point Formulas

End-point formulas are very powerful, very versatile and potentially very dangerous formulas. Only BlueStep employees are allowed to create and edit them. However, knowing a little about their purpose is still useful. End-point formulas are triggered by an HTTP request. They then read and/or modify data in Relate or elsewhere. Then they send an HTTP response. Basically, they are a small scale webserver embedded inside the BlueStep platform and programmed via RelateScript. End-point formulas also have control over database transactions which allows data to be stored in multiple distinct steps.

Basic Syntax and Behavior

Return to top

Relate Script's syntax rules (equivalent to grammar and punctuation rules in English) are mostly based on Java, which is similar to C, C++ and JavaScript. It does, however, borrow some ideas from other types of computer languages, such as SQL.

Syntax Rules and Results

Relate Script is a type-safe language, meaning that the data-type of each formula input and formula result is determined when the formula is written. The formula engine knows, before the formula runs, whether the result will be a date, a number, a piece of text or another specific data type. This allows for detection of common logic problems, such as using a date where a number should be.

Relate Script is also case-sensitive, meaning that it will treat ABCD as a totally different thing from abcd or aBcD. Relate Script follows the same rules for variable names and other identifiers as in most case-sensitive computer languages. Specifically, identifiers must contain only letters, numbers and the underscore character. Also an indentifier cannot begin with a number. All variable names, Relate field formula identifiers, Relate form formula ids, function names and field/property names must meet these naming requirements.

Relate Script allows for simple, single-expression formulas such as x = y + 4, as well as complex, multi-statement formulas with control flow structures and advanced logic. Multiple statements are separated by semi-colons, but the semi-colon can be omitted if it is at the end of a line.

As with most computer languages, whitespace (spaces, tabs, newlines) are mostly ignored. This means that x=4 is the same as

x

=        4

but would be quite silly actually written that way.

Functions can be called with an object-oriented syntax with the first parameter before the function name as in: "asdf".toUpperCase(), or they may be called in the procedural style with all parameters inside the parentheses: toUpperCase("asdf"). Both expressions are equivalent and give the same result: "ASDF".

As the example in the previous rule implies, functions may be called on ANY appropriate value. So the following works (and results in 8): ("qwerty" + "asdf").indexOf("d")

As in JavaScript, strings (pieces of text data) can be single or double quoted: "asdf" == 'asdf' It is important to remember, though, that the closing quote must be the same as the opening one.

Widening conversions work similar to those in Java with regard to Integer/Float. Namely, you can always use an integer value where a floating-point value should be, but not vice-versa.

All of the basic types of data can be null and (as in SQL) nearly any operation with a null as one of the operands will result in null. So 4 + null results in null. The only exceptions are the equality/inequality operators which allow testing for null, string concatenation where null is equivalent to the empty string, and the aggregate functions sum(...), max(...), min(...) and avg(...) where null is generally ignored entirely.

New variables are created by simply assigning them a value. So the statement x = 4 would create a new Integer variable, x, and assign it the value 4. This example assumes that the variable x does not already exist. If x does already exist, then no new variable is created, instead the existing variable is assigned the value 4.

Comments are as in C++/Java: a double slash creates a comment to the end of the line. A block comment in the middle of a line or spanning multiple lines is started with a slash then asterisk and ended with an asterisk then slash. Here are some comment examples:

x = 4 // end of line comment 
// full line comment
y = /* middle of line comment */ 19; 
/* This is a multi-line 
comment */

Style Guidelines

Return to top

Following proper style guidelines can make the meaning of a formula much clearer to a human reader. Conversely, using poor style can make a piece of formula logic difficult to read, even for experienced software developers. Style guidelines do not affect the logic or outcome of a formula, but if followed they can make scripts easier to read, easier to maintain and (with a bit of experience) help to identify problems. Also, although these guidelines cover many cases, there will always be cases that don't fit the guidelines and will need improvisation. The goal is always the same though: style techniques should make the meaning of the logic clearer.

Indentifiers

Formula IDs for fields and forms, as well as variable names, should be carefully chosen. Choose names that give a pretty good idea as to what the identifier is for, even if returning to it after six months. Avoid using very short names that have no clear meaning. It is generally better to spell things out than abbreviate in a way that isn't memorable or understandable later. In this style guide, the examples sometimes seem to "break" this rule by including meaningless variable names such as x and y. But in another sense, the examples are keeping the spirit of the rule because the names are intended to illustrate layout without any other meaning, so using a meaningless name conveys this.

At the same time, avoid using names that are so long that they are cumbersome to type. Excessively long names can also cause trouble by taking up so much room on a line that other parts of the line cannot be seen in the formula edit window. This obscures the meaning of the formula rather than making it clearer.

The most important rule in naming things is this warning: If there are two fields or variables which do similar things be SURE that the names chosen clearly indicate which name is used for what. There are few things that make a piece of logic more difficult to read than two variables with similar names and no clear indication of what the difference is.

Lastly, the use of "camel case" is recommended. Since identifier names cannot contain spaces, it can become difficult to distinguish word boundaries. One such example is a domain which BlueStep purchased and later regretted. It was "answers4therapists.com". Is that 'answers for therapists' or 'answers for the rapists'? Camel caps solves this problem by capitalizing the first letter of each word after the first word. So, in our example it should be answers4Therapists.com not answers4TheRapists.com. With a little practice, oneCanLearnToReadCamelCapsQuiteEasily. Any words which are acronyms should be all caps (or all lowercase, if they are the first word) as in: mySpecialURL

Whitespace

Whitespace is all those symbols which have no glyph to display, but affect the position of the letters, numbers and symbols around them. Most common is the humble "space" character we use to separate words, but there is also the carriage-return (or "enter"), newline and the tab character. Even more exotic is vertical tab which hasn't seen serious use in several decades. Appropriate use of whitespace, as in graphical design, can make all the difference in making a message readable. Here are a few rules:

Put a space before and after most mathematical operators. In writing x=y+z it's not that much more readable as x = y + z, but as the complexity of the logic grows, so does the need for spaces. Consider the following fragment from an actual script:

if (pastBPHigh != null && (diastolic30High == -1 || pastBPHigh > diastolic30High)) {
      diastolic30High = pastBPHigh;
    }

is far more readable than

if(pastBPHigh!=null&&(diastolic30High==-1||pastBPHigh>diastolic30High))
     diastolic30High=pastBPHigh; 

Getting in the habit of doing it right will save much frustration later.

Guidelines for other symbols include: put a space after, but not before, commas and semi-colons.

Do not put a space between a prefix or suffix operator and the thing it is operating on: y * -x,

not y * - x.

Normally, a space is put before an opening parenthesis (but not after), and after a closing parenthesis (but not before.) However, with nested paretheses there should be no space between adjacent parentheses. For example:

y = ((x + 1) * (x + 15))

One final exception is function calls. Do not put a space between the function name and the opening parenthesis:

fullName.indexOf(",") 

However, follow the normal rules and include a space before the opening parenthesis in a statement:

if (eyeColor == "blue")

There should not be spaces around a period or around square brackets, unless another rule would require it

myArray[5] = someForm.someField 

Following an opening brace (that's the curly kind), begin a new line and indent each line after the brace until reaching the matching closing brace. The closing brace should begin a new line and be indented the same amount as the line containing the opening brace. Here is an example:

    if (salsaFlavor == "hot") {
        needWater = true;
        if (chipTexture = "crisp") {
            message = "Tasty!";
        } else {
            message = "We need fresh chips!"
        }
    } else if (salsaFlavor == "mild") {
        message = "Boring.";
    } 

In a very long line, it is helpful to break it into multiple lines. Double indent (or more) each line that was originally part the first line. Also, it is helpful to begin each new line with an operator to clearly indicate that this line belongs with the previous one

 message = '<html>'
              + '<body>'
                + <a href="http://mydomain.bluestep.net'
                + myDestinationURL
                + '">Click Here</a>'
              + '</body>'
            + '</html>'
		

Comments

If it was hard to write, what was done will likely be forgotten before it is revisited. Do everyone a favor and leave a note explaining how it works or why that method was chosen. Also, use comments in large formulas to break the logic into sections by using a comment as a section header. A line reading:

// ------- Begin second part: email notifications ------- 

can be a major time saver when seen later. Use common sense though; putting in too many comments can make it hard to see the important ones (not unlike pointless consumer warning labels).

Summary

Remember, don't try to cut corners in formatting and style. It may be a bit faster to type without the extra whitespace, but in the long run, no one ever regrets good style. There are many, many more style techniques that can aid script readability. Generally, anything done stylistically to make the meaning clearer is a good thing.

Text Data: String

Return to top

A String is a sequence of characters or, simply stated, a piece of text. In Relate Script you can create a new String (a string literal) by enclosing it in either single or double quotation marks. For instance:

"This is a String"  

or

'also a String' 

There is a third way of representing String literal values. You can use double acute characters (it's on the button above the tab button) to start and end a string. This type of string literal is for multi-line String values. All of the special character sequence stuff that is discussed below does not apply to multi-line String literals. A multi-line String allows most of these special characters without character sequences. A multi-line String literal is particularly handy for representing HTML within Relate Script. Here is an example:


    ``
    <div style="float:right">
    <img src="/images/buttons/btn_go.gif" alt="Go" onclick="alert('Go!!!')">
    </div>
    ``
    

There is one special String, the empty String, and it looks like this "" or like this ''. It is a String with a length of 0. It should not be mistaken for null which is not a String at all, but simply the absence of data. Some very important details on this confusing concept of null vs. the empty-string can be found in the section on Relate text and memo fields.

When you create a String in quotes, it cannot contain any line breaks. However, you can put in a line break using a special character sequence called an escape. Below is a list of all possible escapes.

\n Newline
\r Carriage return
\t Tab
\b Backspace
\f Formfeed. This character is a holdover from 'ancient' computer history when all computer output was printed, so Formfeed was used to start a fresh sheet of paper.
\u#### or \U#### A Unicode character where #### is the character's 4-digit hex code
\\ Backslash. Since all of the escapes begin with backslash, double backslash represents an actual backslash
\' Single quote. This escape allows a single quote to be inside a single-quoted String.
\" Double quote. This escape allows a double quote to be inside a double-quoted String.

Strings have only one property and it allows you to find out how many characters are in the String. The property is called length. You would use it like this:

"abcdefg".length 

or for a String variable named x you would write

x.length 

Length cannot be changed via the property, it is read-only.

The most common action using Strings is to combine them to form longer Strings. This is called String concatenation and is discussed in the section on operators. The comparative operators also work with Strings.

There are also many functions which work with Strings. There are functions for searching within a String, extracting a piece of a String and comparing Strings to see which comes first alphabetically. There are functions for converting Strings between upper and lower case, and between HTML and plain text. There are also functions to convert strings to other types of data such as numbers and dates. The descriptions of these functions are found in the section on functions.

Whole Numbers: Integer

Return to top

An Integer is a number without a decimal, known in mathematics as a whole number. In Relate Script, all Integers are 64-bit, meaning they can represent numbers in the range 9223372036854775807 to -9223372036854775808. This is large enough to count all of the milliseconds in 292 million years. In Relate Script, Integer values can also be null, meaning no value or value unknown. Integer values can also be entered in hexidecimal (base 16) format by using the standard "0x" prefix. For instance, 255 can also be written as 0xFF or 0xff. Finally, numbers can be expressed in octal (base 8) format by prepending a zero. So 255 can also be written as 0377.

There are many operations that can be done on Integers, all of which are discussed in the section on operators.

Real Numbers: Float

Return to top

There are many operations that can be done on Floats, and they are discussed in the section on operators.

Geek Note: In Relate Script, all Floats are 64-bit IEEE 754 numbers, meaning they can represent virtually any number to a degree of precision greater than is needed for nearly any practical application (sort of). IEEE 754 numbers are binary representation of numbers. Because of this most numbers cannot be represented perfectly. For instance 0.1 cannot be represented in binary floating point for the same reason that 1/3 cannot be represented with normal decimal numbers you used in school: It has an infinte repeating sequence after the decimal point (or more correctly the floating point). Instead of an actual 0.1 you will get the closest value that can be represented. Formatters can compensate when displaying the number since they round to the nearest decimal digit during conversion to a String value. For instance, this code:

output = "Do some subtraction: " + (3.3 - 2.2); 

Puts the value "Do some subraction: 1.0999999999999996" in the output variable. However, this code:

output = "Do some subtraction: " + format(3.3 - 2.2, "#.#####"); 

Puts the improved value "Do some subtraction: 1.1" in the output variable by limiting the precision of the number to 5 decimal places.

True/False Values: Boolean

Return to top

Boolean values are the simplest of all values. They can be true or false. Yet, with just these two values the entire digital age was born. For a computer, every decision and every computation must come down to a simple true/false, yes/no or 1/0 and that is where Boolean values come in. In Relate Script there is a third possible value, null, meaning unknown or no data.

There are many ways to work with Boolean values, all of which are discussed in the section on operators.

Dates and Times

Return to top

In Relate Script, Date, Time and DateTime are three separate and distinct types. A DateTime differs significantly from independent Date and Time values in that it represents an exact moment in time which must be interpreted via a time zone. Without translation into a time zone value, DateTime values have no meaningful or useful function. In other words, a Date has the same day, month and year values regardless of where in the world you are. A Time has the same hour, minute and second values no matter who is looking at it. A DateTime, on the other hand, will have an hour value that is three hours different if users on the U.S. east coast and west coast each check the value. Even the day, month and year can change depending on the time zone of the person asking. For instance 11:00pm New Year's Eve 2005 in Los Angeles is 2:00am New Year's Day 2006 in Orlando.

Date, Time and DateTime values are comprised of a number of fields or parts. Except where otherwise noted, all fields are of the Integer type and all fields can be both read and modified (assuming the entire value, as a whole, can be modified). They are accessed as dateValue.fieldName. For instance, to get the current year you would use: curDate().year

Field Applies To Description
year Date, DateTime The 4 digit year
month Date, DateTime The month of the year, in which January is 0 and December is 11. I know, it's weird.
date Date, DateTime The day of the month
dayOfYear Date, DateTime The day of the year. For instance Feb 1 is the 32nd day of the year and Dec 31 is the 365th day of the year except on leap-year when it is the 366th.
dayOfWeek Date, DateTime The day of the week, assuming Sunday is 1 and Saturday is 7.
dayOfWeekInMonth Date, DateTime The occurance of the day of the week in the current month. This is used in calculating dates such as the second Thursday of the month or the fourth Saturday of the month. Setting this field to a negative value causes it to count back from the end of the month. So if the day of the week is Saturday and you set dayOfWeekInMonth to -1, the result will be the last Saturday of the month.
weekOfYear Date, DateTime The week of the year where January 1 is always in the first week, and the second week starts with the first Sunday after January 1 and so forth.
weekOfMonth Date, DateTime The week number within the current month. This corresponds to the weeks shown on a standard wall calendar where the first row is the first week, the second row is the second week and so on.
hour Time, DateTime The hour of the morning or afternoon. Midnight and noon are both 0. Other hours of the day are as most people normally think of them.
isAM Time, DateTime A Boolean field indicating whether it is AM or not.
hourOfDay Time, DateTime The hour of the day on a 24 hour clock. Midnight is 0 and 11PM is 23.
minute Time, DateTime The minute within the hour (0-59)
second Time, DateTime The second within the minute (0-59)
millisecond Time, DateTime The millisecond within the second (0-999)
timeInMillis DateTime The number of milliseconds since January 1, 1970 12:00AM GMT. This is useful for certain types of date math.
timeZone DateTime A String value containing the ID of the timezone used when representing the DateTime value. Any time zone identifier supported by Java 6 is possible

To do math with dates, either perform operations on the individual fields or you can use DateDiff values. The operations available are discussed under the arithmetic operators.

There are also several functions available for working with dates and times. The functions can be categorized into two groups: 1) those used for getting the current date and/or time and 2) those used to create a date and/or time from another type of data (for instance, from a piece of text containing a date). These functions are discussed in the section on Functions.

Time Periods: DateDiff

Return to top

A DateDiff represents a period of time or, as the name implies, the difference between two moments in time. DateDiff values are used heavily in many types of date/time math. You can get a DateDiff as the result of subtracting two Date values, two Time values or two DateTime values. You can also create a DateDiff using the toDateDiff function.

DateDiff values are comprised of several fields or parts. All fields can be both read and modified (assuming the entire value of the DateDiff, as a whole, can be modified). They are accessed as dateDiffValue.fieldName. For instance, to get the number of days since date x you would write: (curDate() - x).totalDays

Field Data Type Description
isNegative Boolean Similar to a positive/negative number, dateDiffs can represent a time period going forward or backward in time. Even if isNegative is true, all of the other fields will contain positive values.
years Integer The number of years in this DateDiff, not including any fractional years
months Integer The number of months in this DateDiff, not including any full years or fractional months
days Integer The number of days in this DateDiff, not including any full months/years or any fractional days
hours Integer The number of hours in this DateDiff, not including any full days/months/years or any fractional hours
minutes Integer The number of minutes in this DateDiff, not including any full hours/days/months/years or fractional minutes
seconds Integer The number of seconds in this DateDiff, not including any full minutes/hours/days/months/years or fractional seconds
milliseconds Integer The number of minutes in this DateDiff, not including any full seconds/minutes/hours/days/months/years
totalMillis Integer The total number of milliseconds in this DateDiff, including all larger units of time multiplied out into milliseconds
totalSeconds Float The total number of seconds in this DateDiff including all larger units of time multiplied out into seconds, including fractional seconds
totalMinutes Float The total number of minutes in this DateDiff including all larger units of time multiplied out into minutes, including fractional minutes
totalHours Float The total number of hours in this DateDiff including all larger units of time multiplied out into hours, including fractional hours
totalDays Float The total number of days in this DateDiff including all larger units of time multiplied out into days, including fractional days
totalMonths Float The total number of months in this DateDiff including all larger units of time multiplied out into months, including fractional months
totalYears Float The total number of years in this DateDiff, including fractional years

It is important to know that when converting months to days and vice-versa, the DateDiff internal logic considers a month to be exactly (365.25 / 12) days long, which is the average lengh of a month in a normal four year period. The internal logic attempts to refrain from doing this conversion, but conversion is sometimes unavoidable. This can result in some rather odd paradoxes. For instance, if you ask how many months and days there are between February 1, 2005 and March 2, 2005. You might consider it more accurate to say 29 days rather than 1 month and 1 day. Exactly which result is returned depends on how the DateDiff is created and which fields you query.

Lists of Values: Arrays

Return to top

Relate Script Arrays are lists of key/value pairs. This type of data is referred to in other languages as a map, dictionary, associative array or sparse array. Relate Script arrays can have multiple indexes (be multi-dimensional) and each index can be of any data type. A new array of String values indexed by Integer values would be created as:

 x = new String[Integer] 

You would add elements to the array like this:

x[1] = "Sunday"
x[2] = "Monday"

A multi-dimensional array would be created like this:

x = new String[Integer][Date]   OR   x = new String[Integer, Date] 

You can use Array values to model a mathematical set. For a set of String values you would do this:

mySet = new Boolean[String] 

To add to the set, you would write:

mySet["brown"] = true

To remove, you would say:

mySet["yellow"] = false  OR  mySet["yellow"] = null

To test to see if a particular String value is in the set, you would write

if (mySet["green"] == true) { /* do something */ }

Array values work particularly well with for/in looping statements, which allow you to go through the entries in order by the index. There are also three functions available for Array values: size, firstIndex and lastIndex. These functions are described in the section on functions.

More information and examples can be found on the page titled The Array Creation Statement.

Order of Operations

Return to top

Expressions are evaluated in the order listed below:

Operator(s) Description Evaluation Order
() Parenthesis N/A
. or [] Field/Method Access, Array Access left-to-right
++ or -- Post-Increment/Decrement N/A
- or ++ or -- or ~ or ! or Cast Negate, Pre-Increment, Pre-Decrement, Bitwise Complement, Logical Complement (NOT), Cast right-to-left
* or / or % Multiply, Divide, Remainder left-to-right
- or + Subtract, Add/Concatenate left-to-right
<< or >> or >>> Shift left-to-right
< or > or <= or >= Relational Comparison N/A
== or != Equality, Inequality left-to-right
& Logical/Bitwise AND left-to-right
^ Logical/Bitwise XOR left-to-right
| Logical/Bitwise OR left-to-right
&& Conditional AND left-to-right
|| Conditional OR left-to-right
? : Conditional Operator N/A
= or *= or /= or %= or += or -= or |= or <<= or >>= or >>>= or &= or ^= Assignment right-to-left

Assignment

Return to top

There are nine assignment operators. Some of them have multiple uses depending on the datatypes to which they apply. They are described below in order of most common usage.

Simple Assignment: =

The most basic type of assignment takes the value to the right side of the equal sign and assigns it to the variable or field named on the left of the equal sign. If the variable name on the left of the equals does not exist (is not defined), then assigning it a value for the first time creates/defines the variable. For instance,

x = 4; 

assigns the value 4 to the variable x. If x is not already defined, then a new variable, x, is created and given a type of Integer before being assigned the value 4. You cannot normally create a new variable with a value of null, because null does not have a data type so the new variable cannot be fully defined. However, there are some ways that a new variable can be assigned null. It is possible to cast null to be one of the data types that allow casting. Also, you can assign the result of an expression to a new variable and the expression may evaluate to null.

The result of an assignment (in the rare case it is used as part of a larger expression) is the value of the variable or field after the assignment.

Assignment with concatenate: +=

Assignment with concatenation is by far the most common use of the += operator. Assignment with concatenation essentially directs appending of a new value to the end of a current piece of text. When building large pieces of text, such as when constructing the body of an e-mail message, it is usually necessary to build up a String piece-by-piece, since doing it in a single operation would be extraordinarily long and complex. So, the following is an example of a fairly common logic sequence:

fullName = lastName;
    if (lastName != null && firstName != null) fullName += ", ";
    fullName += firstName;

This code snippet creates a full name value, but only puts a comma between the last and first names if both have a value. If either the first or last name is empty, then the comma is omitted.

Assignment with concatenation requires a String variable or field on the left side, but can have any type of data on the right side. If a non-String value is concatenated to a String, it is first converted to a String using its normal formatting rules, then appended to the String value.

The result of an assignment with concatenation (in the rare case it is used as part of a larger expression) is the value of the variable or field after the assignment.

Assignment with add: +=

Assignment with add retrieves the value on the left of the operator, adds the value on the right, then assigns the result back into the variable or field on the left. It is essentially a shortcut operation which could be written in longer format. For example,

x += 4;

is equivalent to

x = x + 4;

Assignment with add works for Integer and Float values. It also works for Date, Time and DateTime values when adding a DateDiff. Finally, it can be used to add two DateDiff values.

Assign with add works slightly differently with Date, Time and DateTime fields because the result may differ from the result of a normal add. For instance, if you have a date variable, d, and you add 60 to the month field like this:

d.month += 60;

You will not actually change the month of the year. Instead this will add 5 to the year field since 60 months is equal to 5 years.

The result of an assignment with add (in the rare case it is used as part of a larger expression) is the value of the variable or field after the assignment.

Assignment with subtract: -=

Assignment with subtract works identically to assignment with add, except it subtracts instead of adding.

Assignment with divide: /=

Assignment with divide retrieves the value on the left of the operator, divides it by the value on the right, then assigns the result back into the variable or field on the left. It is essentially a shortcut operation which could be written in longer format. For example,

 x /= 4;

is equivalent to

x = x / 4;

Assignment with divide works for a Float value divided by either an Integer or Float. It works for an Integer divided by an Integer, in which case the remainder is discarded. It also works for DateDiff values when divided by an Integer or Float value.

The result of an assignment with divide (in the rare case it is used as part of a larger expression) is the value of the variable or field after the assignment.

Assignment with multiply: *=

Assignment with multiply works essentially the same as assignment with divide, except it multiplies instead of dividing.

Assignment with mod: %=

Assignment with mod works essentially the same as assignment with divide, except it only works with Float and Integer values and stores the remainder of the division rather than the quotient.

Assignment with AND: &=

Assignment with AND retrieves the value on the left of the operator, performs a bitwise or logical AND with the value on the right, then assigns the result back into the variable or field on the left. It is essentially a shortcut operation which could be written in longer format. For example,

x &= 4;

is equivalent to

x = x & 4;

Assignment with AND works only on Integer and boolean values.

The result of an assignment with AND (in the rare case it is used as part of a larger expression) is the value of the variable or field after the assignment.

Assignment with OR: |=

Assignment with OR works essentially the same as assignment with AND, except it performs a bitwise or logical OR instead of an AND operation.

Assignment with XOR: ^=

Assignment with XOR works essentially the same as assignment with AND, except it performs a bitwise or logical XOR instead of an AND operation.

Assignment with left-shift: <<=

Assignment with left-shift retrieves the value on the left of the operator, left-shifts it by the number of bits specified by the value on the right (filling the empty low bits with 0), then assigns the result back into the variable or field on the left. It is essentially a shortcut operation which could be written in longer format. For example,

 x <<= 4;

is equivalent to

 x = x << 4;

Assignment with right-shift works only on Integers.

The result of an assignment with left-shift (in the rare case it is used as part of a larger expression) is the value of the variable or field after the assignment.

Assignment with right-shift: >>=

Assignment with right-shift works essentially the same as assignment with left-shift, except it shifts to the right and propagates the sign-bit.

Assignment with unsigned right-shift: >>>=

Assignment with unsigned right-shift works essentially the same as assignment with left-shift, except it shifts to the right and fills the empty high bits with 0.

String Concatenation

Return to top

String concatenation means combining two String values into one. This is done with the plus symbol as in this example:

"first" + "second"

You can also concatenate other types of data to a String, such as:

"two + two = " + 4

When you do this, the number 4 is first converted to a String, "4", then concatenated to the original String. You can put the thing to be concatenated either before or after the String. For instance,

3.1415 + " is the value of pi"

String concatenation is one of the few operations which does not result in null if one of the input values is null. The only way to get a null result is if both inputs are null. If only one input is null, the result is the other, non-null input.

Arithmetic & Date/Time Operators

Return to top

The arithmetic operators do simple mathematical operations that are familiar to most individuals. They work with numbers (Integer and Float) and are also used to do date math. For all of the arithmetic operators, if one of the input values is null the result will be null.

Addition: +

Addition works with Integer and Float values in the normal way you would expect. If both inputs are Integers then the result is an Integer, otherwise the result is a Float.

Addition also works with DateDiff values added to other DateDiff values, to Date values, to Time values and to DateTime values. If a DateDiff is added to a Date, the result is a Date. If a DateDiff is added to a Time, the result is a Time. For a DateTime, same rule applies. The result is a DateDiff only when a DateDiff is added to another DateDiff. The operands can be in either order and the result is the same. In other words, addition is commutative when doing date and time math.

Subtraction: -

Subtraction works with Integer and Float values in the normal way you would expect. If both inputs are Integers then the result is an Integer, otherwise the result is a Float.

Subtraction also works with DateDiff values subtracted from other DateDiff values, from Date values, from Time values and from DateTime values. If a DateDiff is subtracted from a Date, the result is a Date. If a DateDiff is subtracted from a Time, the result is a Time. For a DateTime, same rule applies. The result is a DateDiff only when a DateDiff is subtracted from another DateDiff. The DateDiff value must be the second operand; in other words, you cannot subtract a Date from a DateDiff, even though you can subtract a DateDiff from a Date.

Finally, subtraction works with two Time values, two Date values or two DateTime values with the result being a DateDiff. This is where the term DateDiff comes from originally. You can subtract in either order, but the results will be different: one result will be the negative of the other.

Negation: -

Negation receives only one input, such as: -x. It works with Integer, Float and DateDiff values. The result is the negative of the original and is of the same data type.

Multiplication: *

Multiplication works with Integer and Float values in the normal way you would expect. If both inputs are Integers, then the result is an Integer, otherwise the result is a Float.

Multiplication also works with DateDiff values multiplied by Integer values or by Float values. The result is always a DateDiff. The operands can be in either order and the result is the same. In other words, multiplication is commutative when doing DateDiff math.

Division: /

Division works with Integer and Float values in the normal way you would expect. If both inputs are Integers, then the result is an Integer and the remainder is discarded, otherwise the result is a Float.

Division also works with DateDiff values divided by Integer values or by Float values. The result is always a DateDiff. The DateDiff value must be the first operand, in otherwords, you cannot divide an Integer by a DateDiff even though you can divide a DateDiff by an Integer.

Mod (Remainder): %

Mod works with Integer and Float values and returns the remainder after doing a division. If both inputs are Integers then the result is an Integer; otherwise the result is a Float. For example 5 divided by 3 is 1, remainder 2; so 5 % 3 is 2. For Float the logic is the same, but it is more complex to think about. For example 7.8 divided by 1.5 is 5, remainder 0.3; so 7.8 % 1.5 is 0.3.

Comparative Operators

Return to top

The six comparative operators are used to determine if two things are equal or if one is greater than (or less than) the other. The result of any comparative operation is Boolean. All comparative operators work with all of the standard data types except Array and Boolean. Only == and != work with Boolean input values and none of the operators work with Array values. All of the comparative operators require that the operands both before and after the operator be of the same data type.

Is Equal: ==

The "is equal" operator results in true if the values on each side are exactly equal, otherwise the result is false. This is one of the few operations in Relate Script which does not result in null even if one or both operands are null.

Is Not Equal: !=

The "is not equal" operator results in false if the values on each side are exactly equal, otherwise the result is true. This is also one of the few operations in Relate Script which does not result in null even if one or both operands are null.

Is Greater Than: >

The "is greater than" operator results in true if the value before the operator is greater than the value after the operator. If the value before the operator equals or is less than the value after the operator, then the result is false. When comparing null values, null is considered to be the least possible value, so null is never greater than any value including null.

Is Less Than: <

The "is less than" operator results in true if the value before the operator is less than the value after the operator. If the value before the operator equals or is greater than the value after the operator, the result is false. When comparing null values, null is considered to be the least possible value, so null is less than any value other than null.

Is Greater Than or Equal: >=

The "is greater than or equal" operator results in true if the value before the operator is greater than or equal to the value after the operator. If the value before the operator is less than the value after the operator, then the result is false. When comparing null values, null is considered to be the least possible value, so null is never greater than or equal to any value other than null.

Is Less Than or Equal: <=

The "is less than or equal" operator results in true if the value before the operator is less than or equal to the value after the operator. If the value before the operator is greater than the value after the operator, then the result is false. When comparing null values, null is considered to be the least possible value, so null is less than or equal to any value.

Logical Operators

Return to top

The logical operators do simple boolean operations. They work with Boolean input values and always have a Boolean result. The logical operators are to Boolean values what the arithmetic operators are to number values.

The AND Operator: &

The AND operation receives two Boolean input values and results in true only if both inputs are true. If either input is null, the result is null. Otherwise the result is false. The following table illustrates:

AND

Input 1
true false null
Input 2 true
false
null
true false null
false false null
null null null

The OR Operator: |

The OR operation receives two Boolean input values and results in true if either input is true and the other is not null. If either input is null, the result is null. Otherwise the result is false. The following table illustrates:

AND

Input 1
true false null
Input 2 true
false
null
true true null
true false null
null null null

The XOR Operator: ^

The XOR operation receives two Boolean input values and results in true only if one input is true and the other is false. If either input is null, the result is null. If both inputs are true or both inputs are false the result is false. The following table illustrates:

AND

Input 1
true false null
Input 2 true
false
null
false true null
true false null
null null null

The Conditional AND Operator: &&

The Conditional AND operation receives two Boolean input values. If the first input is true, the result is the second input. If the first input is false or null, the first input is the result and the second input is not evaluated. This can noticably reduce computation time if a formula is being executed repeatedly. In the following table please note the small, but very significant change from the standard AND operation:

AND

Input 1
true false null
Input 2 true
false
null
true false null
false false null
null false null

The Conditional OR Operator: ||

The Conditional OR operation receives two Boolean input values. If the first input is false, the result is the second input. If the first input is true or null, the first input is the result and the second input is not evaluated. This can noticably reduce computation time if a formula is being executed repeatedly. In the following table please note the small, but very significant change from the standard OR operation:

AND

Input 1
true false null
Input 2 true
false
null
true true null
true false null
true null null

The NOT Operator: !

The NOT operation receives only one Boolean input and returns its opposite. So !true results in false, and !false results in true. If the input is null, then the result is null also. This operation is equivalent to negation for a number value.

Casting Operators

Return to top

Casting means to force a data value of one type to be another data type. You can cast a Float to an Integer and vice versa, and you can cast anything to a String. The cast to String is implicit in a String concatenation operation. For instance,

 x = (Float)4;

causes x to be assigned the floating-point value 4.0 instead of the Integer value 4.

Also,

y = (Integer)3.75;

causes y to be assigned the Integer value 3 with the digits after the decimal truncated.

Casting to a String is done the same way. This statement,

z = (String)3 + 4;

assigns the value "34" instead of 7 to the variable z because converting the 3 to a String also makes the + operator a concatenation instead of an addition. The concatenation implicitly casts the 4 to a String as well. So the actual operation performed is "3" + "4".

Example: Using Casting to do Rounding

One thing that can be done with casting is rounding to a certain number of decimal places. However, in practice the round(...) function is better. The formula to round the variable x to 2 places after the decimal is:

x = ((Integer)(x * 100 + 0.5)) / 100.0;

This formula first shifts the decimal two places to the left, then adds 0.5 to do the rounding. It then truncates any digits after the decimal by casting to Integer. After that the decimal is shifted back two places to the right while simultaniously converting back to a Float by dividing by 100.0. However, there is one problem with this formula: it only works correctly for positive numbers. For negative numbers you need:

 x = ((Integer)(x * 100 - 0.5)) / 100.0;

To work for both positive and negative numbers you could do this:

if (x > 0) {
        x = ((Integer)(x * 100 + 0.5)) / 100.0;
    } else {
        x = ((Integer)(x * 100 - 0.5)) / 100.0;
    }
		

Or you can us the conditional operator to do this:

x = ((Integer)(x * 100 + (x > 0 ? 0.5 : -0.5))) / 100.0;

Increment and Decrement Operators

Return to top

The increment and decrement operators work on Integer or Float variables and fields. Each operator causes the number stored to either increase or decrease by 1.

Pre-increment: ++x

The pre-increment operator increases by 1 the value stored in x (where x is some variable or field). If the result of this operation is used in a larger expression, it will be equal to x AFTER incrementing. In other words, increment x, then use the new value in the expression.

Pre-decrement: --x

The pre-decrement operator decreases by 1 the value stored in x (where x is some variable or field). If the result of this operation is used in a larger expression, it will be equal to x AFTER decrementing. In other words, decrement x, then use the new value in the expression.

Post-increment: x++

The post-increment operator increases by 1 the value stored in x (where x is some variable or field). If the result of this operation is used in a larger expression, it will be equal to x BEFORE incrementing. In other words, get the value of x and use that value in the expression, then increment x.

Post-decrement: x--

The post-decrement operator decreases by 1 the value stored in x (where x is some variable or field). If the result of this operation is used in a larger expression, it will be equal to x BEFORE decrementing. In other words, get the value of x and use that value in the expression, then decrement x.

Examples:

What does all of this pre-increment vs. post-increment stuff mean? Here are some examples which should clarify (or at least clarify how confusing these operators can be):

x = 1;
a = x++ - x;
b = ++x - x;
c = x - ++x;
d = x - x++;
e = ++x++;

What are the values of a, b, c, d and e? Let's go through this one line at a time.

At the beginning of the second line, x is equal to 1. According to order-of-operations the post-increment operation happens first so 1 is added to x and the value of x prior to the increment is used as the first value in the subtraction. Next the subtraction occurs, but since x is already incremented, we are actually subracting 2 from 1. The result of -1 is then assigned to the variable a.

In the next line x is once again incremented by 1, but this time the value of x after the increment is used in the subtraction. As before the new value of x is also used on the right side of the subtraction, so 3 minus 3 is assigned to the variable b.

In the fourth line things get really weird. You might think that since the increment has highest precedence that adding one to x is the very first thing that happens. This is not actually the case. The first thing that happens is the value of x is retrieved as the left value of the minus operation. The value retrieved is, of course, 3. Then the minus operation retrieves its right value and the increment, having precedence, occurs with the result being 4. So c is assigned 3 minus 4.

The fifth line works like the fourth, except the value of x prior to the increment is used in the minus operation, so d is assigned 4 minus 4. And x is now equal to 5.

The final line is not valid and will result in an error when it is written. This is because the result of ++x is the value of x after the increment, not x itself. The value of x is an Integer, but x is a variable of type Integer. The error results because you can add 1 to x and assign it back into x with a result of 6, but you cannot add 1 to 6 then assign it back to 6. The addition could work, but the assignment is impossible.

The Conditional Operator

Return to top

Syntax:

condition ? true-result : false-result

The condition is a Boolean expression. The values true-result and false-result must be of the same data type. If the condition's result is true then the result of the conditional operator is true-result and false-result is not evaluated. If the condition's result is false then the result of the conditional operator is false-result and true-result is not evaluated. If the condition's result is null then the result of the conditional operator is null and neither true-result nor false-result is evaluated. For an example of how a conditional operator may be used, refer to the example at the end of the Casting Operators page.

Bitwise Operators

Return to top

The full complement of standard bitwise operations are supported in Relate Script. To explain how they work is beyond the scope of this document, therefore, only a quick summary is included here. Bitwise operations work on Integers. Integers in Relate Script are signed and 64-bit. The operators are:

Operator Description
& Bitwise AND
| Bitwise OR
^ Bitwise XOR
~ Bitwise NOT or 1's Complement
<< Left-Shift
>> Right-Shift with Sign Propagation.
>>> Unsigned Right-Shift

The if Statement

Return to top

Syntax:

if ( boolean-expression ) statement if ( boolean-expression ) statement else statement

In the above syntax description, the single statement may be replaced with multiple statements by enclosing those statements in a statement block using braces:

{ statement1; statement2; statement3; }

Description:

The if statement conditionally executes one or more statements. If the boolean-expression evaluates to true, then the statement following the closing parenthesis is executed. If the boolean-expression evaluates to false, then the statement following the else is executed (if present). If the boolean-expression evaluates to null, then neither statement is executed.

The while Loop Statement

Return to top

Syntax:

while ( boolean-expression ) body-statement

In the above syntax description, the single statement may be replaced with multiple statements by enclosing those statements in a statement block using braces:

{ statement1; statement2; statement3; }

Description:

The while statement repeatedly executes the body-statement as long as the boolean-expression evaluates to true, up to a maximum of 10,000 executions. If the boolean-expression evaluates to false or null, looping stops and the formula continues by executing any statements after the do statement. Note that the body-statement may never be executed if the boolean-expression does not evaluate to true the first time it is evaluated. If the boolean-expression is omitted, then it is considered to be true every time.

The do/while Loop Statement

Return to top

Syntax:

do body-statement while ( boolean-expression );

In the above syntax description, the single statement may be replaced with multiple statements by enclosing those statements in a statement block using braces:

{ statement1; statement2; statement3; }

Description:

The do/while statement repeatedly executes the body-statement as long as the boolean-expression evaluates to true up to a maximum of 10,000 executions. If the boolean-expression evaluates to false or null, looping stops and the formula continues by executing any statements after the do/while statement. Note that the body-statement will always be executed at least once since the boolean-expression is not evaluated until after the first execution. If the boolean-expression is omitted, then it is considered to be true every time.

The for Loop Statement

Return to top

Syntax:

for ( initializers ; boolean-expression ; updaters ) body-statement

In the above syntax description, the single statement may be replaced with multiple statements by enclosing those statements in a statement block using braces:

{ statement1; statement2; statement3; }

Description:

The for statement repeatedly executes the body-statement as long as the boolean-expression evaluates to true up to a maximum of 10,000 executions. If the boolean-expression evaluates to false or null, looping stops and the formula continues by executing any statements after the do statement. Note that the body-statement may never be executed if the boolean-expression does not evaluate to true the first time it is evaluated. If the boolean-expression is omitted, then it is considered to be true every time. The initializers clause is executed exactly once at the beginning of the loop before the boolean-expression is evaluated for the first time and before the body statement is executed. The updaters clause is executed once for each execution of the body statement. It is executed after the body statement and before the re-evaluation of the boolean expression.

The for/in Loop Statement

Return to top

Syntax:

for ( variable-name in array-value ) body-statement for ( variable-name , variable-name2 in array-value ) body-statement

In the above syntax description, the single statement may be replaced with multiple statements by enclosing those statements in a statement block using braces:

{ statement1; statement2; statement3; }

Description:

The for/in statement repeatedly executes the body-statement once for each item in the array-value. Before each execution, the first loop variable is assigned the next index value of the array beginning with the first index value and going through all subsequent index values until the last index value is reached. If a second loop variable is given then it is assigned the value in the array corresponding to the index of the first loop variable. Note that the body-statement may never be executed if the array-value is empty. If the array-value has multiple indexes, the first index is used and the second variable is assigned the sub-array. More information, including an example, can be found under The Array Creation Statement. Note: the for/in loop is the only looping statement that allows more than 10,000 iterations without using nested loops.

The continue Statement

Return to top

Syntax:

continue

Description:

The continue statement must be inside a statement block that is the body-statement of a looping statement. When the continue statement is executed, any statements after it are skipped, and the loop's condition is immediately evaluated to determine if looping should continue (except in the case of a for loop where the updaters are still executed before the condition is re-evaluated).

The break Statement

Return to top

Syntax:

break

Description:

The break statement must be inside a statement block that is the body-statement of a looping statement. When the break statement is executed, any statements after it are skipped, looping is stopped and execution continues with any statements after the loop.

The Array Creation Statement

Return to top

Syntax:

variable-name = new result-type index-type-list

The index-type-list is a list of one or more type names expressed in either of two syntaxes (or a mix of both, if the mood suits you):

[ type-name, type-name, type-name ]

or

[ type-name ][ type-name ][ type-name ]

Description:

The array creation statement creates a new empty array. The array has a result type, and one or more index types. Valid types are: String, Integer, Float, Date, DateTime, Time, DateDiff and Boolean. Note that any of these types may be used as an index type. In many languages this would be called a map, dictionary or sparse array. The firstIndex() and lastIndex() functions, as well as the for/in loop, retrieve the values from the array based on the sorted order of the index values, not based on the order they were added.

Example:

For the following short program:


usHistory = new String[Date];
usHistory[toDate("12/15/1791")] = "Bill of Rights Ratified";
usHistory[toDate("07/04/1776")] = "Independence Day";
usHistory[toDate("06/21/1788")] = "U.S. Constitution is Ratified";
usHistory[toDate("10/19/1781")] = "U.S. Wins Revolutionary War";
outStr = "";
for (date in usHistory) {
    outStr += date + " - " + usHistory[date] + "\n";
}
	

Alternatively, the last three lines could be written with the more advanced looping syntax like so:


for (date, event in usHistory) {
    outStr += date + " - " + event + "\n";
}	
	

With either looping syntax result in outStr would be:


07/04/1776 - Independence Day
10/19/1781 - U.S. Wins Revolutionary War
06/21/1788 - U.S. Constitution is Ratified
12/15/1791 - Bill of Rights Ratified	
	

charCode(...)

Return to top

The charCode(...) function has four variations, not counting syntax variations. It is used to get the character code or codepoint of a character within a String. A character code, or more formally a codepoint, is the numeric value assigned to each character in a charset.

Syntax:

charCode( source-string )
charCode( source-string, position )
charCode( source-string, position, charset-name )
charCode( source-string, charset-name )
source-string.charCode()
source-string.charCode( position )
source-string.charCode( position, charset-name )
source-string.charCode( charset-name )
Parameter Description
source-string The String value containing the character from which the character code will be computed. If null or empty, then the result of the function will be null.
position The index of the character within the source-string from which the character code will be computed. If omitted or null, the first character (at index 0) will be used. If position is negative, the the index will be computed from the end of the String. Thus, -1 is the position of the last character. If position is out-of-bounds, meaning greater than or equal to the length or less than the negative of the length, then the result of the function will be null.
charset-name The name of the charset from which to compute the character code or codepoint. If the charset-name is null or omitted, then the default is Unicode.

Error Results

There are several conditions which can make it impossible to compute a codepoint for a character. In these cases the function results in a negative number. Different negative result values indicate different problems as described below:

Result Description
-1 The character at the position indicated is a high-surrogate and is the last character in the string. String values in Relate script support full 32-bit unicode. However, the actual characters in a String value are 16-bits in size. To make this work, some unicode characters must be represented as a sequence of two 16-bit characters called a surrogate pair. If the 16-bit character indicated by position is the first part of a surrogate pair, known as a high-surrogate, and is positioned at the end of the source-string then an error code of -1 is returned.
-2 The character at the position indicated is a high-surrogate and the next character does not complete the surrogate pair.
-3 The character at the position indicated does not have a character code value in the charset specified. All charsets except unicode have less than 32-bit character codes. Thus they can only represent some subset of the values possible in unicode. If the unicode character chosen is not a part of the subset of characters that can be represented by the charset chosen, then the error code -3 is returned.
-4 The charset specified is not a valid charset name, or is not a supported charset. A list of supported character sets can be found in Sun Microsystem's Java documentation.

charVal(...)

Return to top

The charVal(...) function has two variations, not counting syntax variations. It is used to get a character given a character code or codepoint value. A character code, or more formally a codepoint, is the numeric value assigned to each character in a charset.

Syntax:

charVal( character-code )
charVal( character-code, charset-name )
character-code.charVal()
character-code.charVal( charset-name )
Parameter Description
character-code The integer value containing the character code or codepoint of the character to be computed. If character-code is null, the result of the function will be null.
charset-name The name of the charset from which to compute the character value. If the charset-name is null or omitted, then the default is Unicode.

Error Results

There are several conditions which can make it impossible to compute a character value from a character code. In these cases the function result is a message indicating what problem occured.

Result Description
"INVALID_CHARACTER" The character code given does not represent a valid character in the charset chosen. For instance, negative numbers and numbers greater than the size of the charset will have this result.
"INVALID_CODEPOINT" The character code given does not represent a valid character in Unicode. Certain ranges of codepoints are marked unused in the Unicode specification. Also negative numbers and numbers greater than the maximum value of a unsigned 32-bit integer will have this result when no charset is chosen.
"INVALID_CHARSET" The charset specified is not a valid charset name, or is not a supported charset. A list of supported character sets can be found in Sun Microsystem's Java documentation.

format(...)

Return to top

The format(...) function has five variations, not counting syntax variations, corresponding to the five types of data that it can format. The format function converts numeric values and date/time values into String values using a specific format.

Syntax:

format( integer-value, numeric-format-string )
format( float-value, numeric-format-string )
format( date-value, datetime-format-string )
format( time-value, datetime-format-string )
format( dateTime-value, datetime-format-string )
integer-value.format( numeric-format-string )
float-value.format( numeric-format-string )
date-value.format( datetime-format-string )
time-value.format( datetime-format-string )
dateTime-value.format( datetime-format-string )
Parameter Description
integer-value The Integer value to be formatted and converted to a String value.
float-value The Float value to be formatted and converted to a String value.
numeric-format-string The format string for Integer and Float values determines the format of the result. For a description of possible values refer to the Java API documentation for DecimalFormat.
date-value The Date value to be formatted and converted to a String value.
time-value The Time value to be formatted and converted to a String value.
dateTime-value The DateTime value to be formatted and converted to a String value.
datetime-format-string The format string for Date, Time and DateTime values determines the format of the result. For a description of possible values refer to the Java API documentation for SimpleDateFormat.

Example:

To format a float as a currency, displaying negative numbers like $(45.50), use the format string "$#,##0.00;$(#,##0.00)".

formatElapsedTime(...)

Return to top

The formatElapsedTime(...) function has four variations, not counting syntax variations, corresponding to the two types of data that it can format plus a short/long format option. The format function converts Integer values and DateDiff values into String values indicating elapsed time.

Syntax:

formatElapsedTime( integer-value )
formatElapsedTime( datediff-value )
formatElapsedTime( integer-value, abbrev )
formatElapsedTime( datediff-value, abbrev )
integer-value.formatElapsedTime( )
datediff-value.formatElapsedTime( )
integer-value.formatElapsedTime( abbrev )
datediff-value.formatElapsedTime( abbrev )
Parameter Description
integer-value An Integer value representing elapsed milliseconds to be formatted and converted to a String value.
datediff-value The DateDiff value to be formatted and converted to a String value.
abbrev A boolean value indicating whether abbreviated units of measure should be used. The default is true.

Example:

To display the time elapsed since a particular timestamp use: formatElapsedTime(curDateTime() - timestamp);

getFormatter(...)

Return to top

The getFormatter(...) function has two variations, not counting syntax variations. It is used to format and parse multiple values to/from a String value. This is specifically designed for localization, but it has a wide array of other uses. The result of getFormatter(...) is a formatter object, described in the second part of this page.

Syntax:

getFormatter( format-string )
getFormatter( format-string, time-zone )
format-string.getFormatter()
format-string.getFormatter( time-zone )
Parameter Description
format-string The string containing all of the formatting settings. View Java's MessageFormat documentation for more information. The BlueStep version of this function has the additional format type of 'datetime' in addition to 'date' and 'time'. If the format-string is invalid, a message will be available in the error field of the formatter object. Also, the choice format allows String and Boolean values to be chosen and supports null and default values.
time-zone The time zone identifier as in other date/time functions. This defaults to the current user's preferred time zone.

The Formatter Object

Field/Function Description
set(pos, value) Sets the value of one data placeholders.
format(...) Formats the data values using the format-string given. This method optionally receives a list of values to set starting with position 0 and incrementing with each additional parameter included. This function results in a String value. If the String value is null, then something went wrong. Check the error field to find out what happened.
parse(mssg) Parses data values out of mssg and puts them in the data fields of this formatter. The parse values can be retrieved via the get___(pos) functions listed below. This function results in true if the parse was successful and false if there was a failure. To find out what went wrong, check the error field.
getBoolean(pos) Gets the value at the indicated position as a Boolean. If the value at the indicated position is not a boolean, this function will attempt conversion.
getDate(pos) Gets the value at the indicated position as a Date. If the value at the indicated position is not a date/time, this function will result in null.
getTime(pos) Gets the value at the indicated position as a Time. If the value at the indicated position is not a date/time, this function will result in null.
getDateTime(pos) Gets the value at the indicated position as a DateTime. If the value at the indicated position is not a date/time, this function will result in null.
getInteger(pos) Gets the value at the indicated position as an Integer. If the value at the indicated position is not a number, this function will result in null.
getFloat(pos) Gets the value at the indicated position as a Float. If the value at the indicated position is not a number, this function will result in null.
getString(pos) Gets the value at the indicated position as a String. If the value at the indicated position is not a string, this function will attempt to convert the value to a String.
error This is a read-only String value possibly containing a message if a previous operation failed and the error has not been cleared since.
clearError( ) Clears the error field back to null. This can be useful when you wish to use the same formatter object to attempt another operation following an error.

Examples:

Suppose we need a formula to produce the body of an HTML email reminding the user of an appointment. A formatter can make the job both easier to write and easier to read and maintain:


	body = getFormatter('{0} {1}:

This email is to notify you of ' +'your {2} appointment at {3} on {4}

'); title = info.isMale ? "Mr." : "Ms."; sendEmail( userEmail, 'clientcare@bluestep.net', 'Appointment Reminder', body.format( title, name.last, appointment.type, appointment.time.format("h:mma"), appointment.time.format("MM/dd/yyyy")), true )

This example can be simplified by letting the formatter do more of the formatting for you instead of just inserting pre-formatted values. For instance the appointment time can be automatically formatted as a time, then a date. By doing this you can also insert the appointment time into your result twice from the same input, but with different formats. Finally, the title can be calculated using a choice formatter.


	body = getFormatter('{0,choice,true#Mr.|false#Ms.} {1}:

' + 'This email is to notify you of your {2} appointment at {3,time} on {3,date}' + '

'); sendEmail( userEmail, 'clientcare@bluestep.net', 'Appointment Reminder', body.format(info.isMale, name.last, appointment.type, appointment.time), true )

The next example begins to explore the true power of formatters. The primary purpose of formatters is to be able to change format without changing logic and to change format dynamically without writing separate logic for each format. In the following examples we'll look at displaying a list of data in two ways: once in table layout and once as a list with bullet points. Notice that in the code the ONLY difference is the formatters, but the output is very, very different. The examples assume a medication system with prescribed medications and administration records.

Example A:table layout


	blockFormat = getFormatter(''
  + ''
  + ''
  + ''
  + '{0}
MedScheduleByTime
') rowFormat = getFormatter('' + '{0}' + '{1}' + '{2}' + '{3,datetime}'); rows = new String[Integer]; for (i, mar in mars) { med = meds.getById(mar.medId); if (mar.adminSig.timestamp != null) { rows[i] = rowFormat.format( med.name, mar.schedAdmin.selectedName, mar.adminSig.name, mar.adminSig.timestamp ); } } result = blockFormat.format(rows.join("\n"));

Possible Result:

Med Schedule By Time
Enalapril 8:00AM Betty Roberts 12/29/2011 08:05AM
Prevacid 8:00AM Betty Roberts 12/29/2011 08:05AM
Niaspan Bedtime Helen Childs 12/29/2011 10:27PM
Acetaminophen PRN Patrick Smith 12/29/2011 04:42PM
blockFormat = getFormatter('
    {0}
') rowFormat = getFormatter('
  • {1} {0} by: {2} ({3,time})
  • '); rows = new String[Integer]; for (i, mar in mars) { med = meds.getById(mar.medId); if (mar.adminSig.timestamp != null) { rows[i] = rowFormat.format( med.name, mar.schedAdmin.selectedName, mar.adminSig.name, mar.adminSig.timestamp ); } } result = blockFormat.format(rows.join("\n"));

    Possible Result:

    join(...)

    Return to top

    parseFloat(...)

    Return to top

    parseInteger(...)

    Return to top

    readCSV(...)/readCSVRows(...)

    Return to top

    split(...)

    Return to top

    toDate(...)

    Return to top

    toDateDiff(...)

    Return to top

    toDateTime(...)

    Return to top

    toTime(...)

    Return to top
    Return to top

    © 2017 Vorro, Inc.

    Have Questions? Call Client Care at 801-336-3043.