- JavaFX Script Basics
- JavaFX Script Language
- Class Declaration
- Mixin Classes
- Object Literals
- Variables
- Sequences
- Functions
- Strings
- Expressions and Operators
- Chapter Summary
Expressions and Operators
Block Expression
A block expression is a list of statements that may include variable declarations or other expressions within curly braces. If the last statement is an expression, the value of a block expression is the value of that last expression; otherwise, the block expression does not represent a value. Listing 3.6 shows two block expressions. The first expression evaluates to a number represented by the subtotal value. The second block expression does not evaluate to any value as the last expression is a println() function that is declared as a Void.
Listing 3.6. Block Expressions
// block expression with a value var total = { var subtotal = 0; var ndx = 0; while(ndx < 100) { subtotal += ndx; ndx++; }; subtotal; // last expression }; //block expression without a value { var total = 0; var ndx = 0; while(ndx < 100) { total += ndx; ndx++; }; println("Total is {total}"); }
Exception Handling
The throw statement is the same as Java and can only throw a class that extends java.lang.Throwable.
The try/catch/finally expression is the same as Java, but uses the JavaFX syntax:
try { } catch (e:SomeException) { } finally { }
Operators
Table 3.2 contains a list of the operators used in JavaFX. The priority column indicates the operator evaluation precedence, with higher precedence operators in the first rows. Operators with the same precedence level are evaluated equally. Assignment operators are evaluated right to left, whereas all others are evaluated left to right. Parentheses may be used to alter this default evaluation order.
Table 3.2. Operators
Priority |
Operator |
Meaning |
1 |
++/-- (Suffixed) |
Post-increment/decrement assignment |
2 |
++/-- (Prefixed) |
Pre-increment/decrement assignment |
- |
Unary minus |
|
not |
Logical complement; inverts value of a Boolean |
|
sizeof |
Size of a sequence |
|
reverse |
Reverse sequence order |
|
indexof |
Index of a sequence element |
|
3 |
/, *, mod |
Arithmetic operators |
4 |
+, - |
Arithmetic operators |
5 |
==, != |
Comparison operators (Note: all comparisons are similar to isEquals() in Java) |
<, <=, >, >= |
Numeric comparison operators |
|
6 |
instanceof, as |
Type operators |
7 |
and |
Logical AND |
8 |
or |
Logical OR |
9 |
+=, -=, *=, /= |
Compound assignment |
10 |
=>, tween |
Animation interpolation operators |
11 |
= |
Assignment |
Conditional Expressions
if/else
if is similar to if as defined in other languages. First, a condition is evaluated and if true, the expression block is evaluated. Otherwise, if an else expression block is provided, that expression block is evaluated.
if (date == today) { println("Date is today"); }else { println("Out of date!!!"); }
One important feature of if/else is that each expression block may evaluate to an expression that may be assigned to a variable:
var outOfDateMessage = if(date==today) "Date is today" else "Out of Date";
Also the expression blocks can be more complex than simple expressions. Listing 3.7 shows a complex assignment using an if/else statement to assign the value to outOfDateMessage.
Listing 3.7. Complex Assignment Using if/else Expression
var outOfDateMessage = if(date==today) { var total = 0; for(item in items) { total += items.price; } totalPrice += total; "Date is today"; } else { errorFlag = true; "Out of Date"; };
In the previous example, the last expression in the block, the error message string literal, is the object that is assigned to the variable. This can be any JavaFX Object, including numbers.
Because the if/else is an expression block, it can be used with another if/else statement. For example:
var taxBracket = if(income < 8025.0) 0.10 else if(income < 32550.0)0.15 else if (income < 78850.0) 0.25 else if (income < 164550.0) 0.28 else 0.33;
Looping Expressions
For
for loops are used with sequences and allow you to iterate over the members of a sequence.
var daysOfWeek : String[] = [ "Sunday", "Monday", "Tuesday" ]; for(day in daysOfWeek) { println("{indexof day}). {day}"); }
To be similar with traditional for loops that iterate over a count, use an integer sequence range defined within square brackets.
for( i in [0..100]} {
The for expression can also return a new sequence. For each iteration, if the expression block executed evaluates to an Object, that Object is inserted into a new sequence returned by the for expression. For example, in the following for expression, a new Text node is created with each iteration of the day of the week. The overall for expression returns a new sequence containing Text graphical elements, one for each day of the week.
var textNodes: Text[] = for( day in daysOfWeek) { Text {content: day }; }
Another feature of the for expression is that it can do nested loops. Listing 3.8 shows an example of using nested loops.
Listing 3.8. Nested For Loop
class Course { var title: String; var students: String[]; } var courses = [ Course { title: "Geometry I" students: [ "Clarke, "Connors", "Bruno" ] }, Course { title: "Geometry II" students: [ "Clarke, "Connors", ] }, Course { title: "Algebra I" students: [ "Connors", "Bruno" ] }, ]; for(course in courses, student in course.students) { println("Student: {student} is in course {course}"); }
This prints out:
Student: Clarke is in course Geometry I Student: Connors is in course Geometry I Student: Bruno is in course Geometry I Student: Clarke is in course Geometry II Student: Connors is in course Geometry II Student: Connors is in course Algebra I Student: Bruno is in course Algebra I
There may be zero or more secondary loops and they are separated from the previous ones by a comma, and may reference any element from the previous loops.
You can also include a where clause on the sequence to limit the iteration to only those elements where the where clause evaluates to true:
var evenNumbers = for( i in [0..1000] where i mod 2 == 0 ) i;
while
The while loop works similar to the while loop as seen in other languages:
var ndx = 0; while ( ndx < 100) { println("{ndx}"); ndx++; }
Note that unlike the JavaFX for loop, the while loop does not return any expression, so it cannot be used to create a sequence.
Break/Continue
break and continue control loop iterations. break is used to quit the loop altogether. It causes all the looping to stop from that point. On the other hand, continue just causes the current iteration to stop, and the loop resumes with the next iteration. Listing 3.9 demonstrates how these are used.
Listing 3.9. Break/Continue
for(student in students) { if(student.name == "Jim") { foundStudent = student; break; // stops the loop altogether, //no more students are checked } } for(book in Books ) { if(book.publisher == "Addison Wesley") { insert book into bookList; continue; // moves on to check next book. } insert book into otherBookList; otherPrice += book.price; }
Type Operators
The instanceof operator allows you to test the class type of an object, whereas the as operator allows you to cast an object to another class. One way this is useful is to cast a generalized object to a more specific class in order to perform a function from that more specialized class. Of course, the object must inherently be that kind of class, and that is where the instanceof operator is useful to test if the object is indeed that kind of class. If you try to cast an object to a class that that object does not inherit from, you will get an exception.
In the following listing, the printLower() function will translate a string to lowercase, but for other types of objects, it will just print it as is. First, the generic object is tested to see if it is a String. If it is, the object is cast to a String using the as operator, and then the String’s toLowerCase() method is used to convert the output to all lowercase. Listing 3.10 illustrates the use of the instanceof and as operators.
Listing 3.10. Type Operators
function printLower(object: Object ) { if(object instanceof String) { var str = object as String; println(str.toLowerCase()); }else { println(object); } } printLower("Rich Internet Application"); printLower(3.14);
Accessing Command-Line Arguments
For a pure script that does not declare exported classes, variables, or functions, the command-line arguments can be retrieved using the javafx.lang.FX.getArguments():String[] function. This returns a Sequence of Strings that contains the arguments passed to the script when it started. There is a another version of this for use in other invocations, such as applets, where the arguments are passed using name value pairs, javafx.lang.FX.getArguments(key:String):String[]. Similarly, there is a function to get system properties, javafx.lang.FX.getProperty(key:String):String[].
If the script contains any exported classes, variables, or functions, arguments are obtained by defining a special run function at the script level.
public function run(args:String[] ) { for(arg in args) { println("{arg}"); } }
Built-in Functions and Variables
There are a set of functions that are automatically available to all JavaFX scripts. These functions are defined in javafx.lang.Builtins.
You have already seen one of these, println(). Println() takes an object argument and prints it out to the console, one line at a time. It is similar to the Java method, System.out.println(). Its companion function is print(). Print() prints out its argument but without a new line. The argument’s toString() method is invoked to print out a string.
println("This is printed on a single line"); print("This is printed without a new line");
Another function from javafx.lang.Builtins is isInitialized(). This method takes a JavaFX object and indicates whether the object has been completely initialized. It is useful in variable triggers to determine the current state of the object during initialization. There may be times that you want to execute some functionality only after the object has passed the initialization stage. For example, Listing 3.11 shows the built-in, isInitialized() being used in an on replace trigger.
Listing 3.11. isInitialized()
public class Test { public var status: Number on replace { // will not be initialized // until status is assigned a value if(isInitialized(status)) { commenceTest(status); } } public function commenceTest(status:Number) : Void { println("commenceTest status = {status}:); } }
In this example, when the class, Test, is first instantiated, the instance variable, status, first takes on the default value of 0.0, and then the on replace expression block is evaluated. However, this leaves the status in the uninitialized state. Only when a value is assigned to status, will the state change to initialized. Consider the following:
var test = Test{}; // status is uninitialized test.status = 1; // now status becomes initialized
In this case when Test is created using the object literal, Test{}, status takes on the default value of 0.0; however, it is not initialized, so commenceTest will not be invoked during object creation. Now when we assign a value to status, the state changes to initialized, so commenceTest is now invoked. Please note that if we had assigned a default value to status, even if that value is 0, then status immediately is set to initialized. The following example demonstrates this.
public class Test { public var status: Number = 0 on replace { // will be initialized immediately. if(isInitialized(status)) { commenceTest(status); } }
The last built-in function is isSameObject(). isSameObject() indicates if the two arguments actually are the same instance. This is opposed to the == operator. In JavaFX, the == operator determines whether two objects are considered equal, but that does not mean they are the same instance. The == operator is similar to the Java function isEquals(), whereas JavaFX isSameObject is similar to the Java == operator. A little confusing if your background is Java!
The built-in variables are __DIR__ and __FILE__. __FILE__ holds the resource URL string for the containing JavaFX class. __DIR__ holds the resource URL string for directory that contains the current class. For example,
println("DIR = {__DIR__}"); println("FILE = {__FILE__}"); // to locate an image var image = Image { url: "{__DIR__}images/foo.jpeg" };
The following examples show the output from a directory based classpath versus using a JAR-based class path.
Using a Jar file in classpath $javafx -cp Misc.jar misc.Test DIR = jar:file:/export/home/jclarke/Documents/ Book/FX/code/Chapter3/Misc/dist/Misc.jar!/misc/ FILE = jar:file:/export/home/jclarke/Documents/ Book/FX/code/Chapter3/Misc/dist/Misc.jar!/misc/Test.class Using directory classpath $ javafx -cp . misc.Test DIR = file:/export/home/jclarke/Documents/Book/ FX/code/Chapter3/Misc/dist/tmp/misc/ FILE = file:/export/home/jclarke/Documents/Book/ FX/code/Chapter3/Misc/dist/tmp/misc/Test.class