Binding Java Objects in JavaFX Script

Bindings are one of the key features of the JavaFX language, because they simplify one´s life tremendously. By reducing the amount of boilerplate code, which is usually needed to keep the objects in an application in-sync, the developer can concentrate on the really important things. (You know, like a cool looking UI.) 😉

Unfortunately Java objects do not provide this capabilities for their fields. That means the boilerplate code has to be written manually. This article will show a solution to bind properties of Java objects.

Overview

Let´s assume, we have a Java class Rectangle with the four properties x, y, width, and height. We want to show the rectangle on the screen using JavaFX script and update its position and size as the properties of the Java object change.

The code of the class Rectangle would be something like this:

 1 public class MyRectangle {
 2     private int x, y;
 3     private int width, height;
 4
 5     public int getX() { return x; }
 6     public void setX(int x) { this.x = x; }
 7     public int getY() { return y; }
 8     public void setY(int y) { this.y = y; }
 9     public int getWidth() { return width; }
10     public void setWidth(int width) { this.width = width; }
11     public int getHeight() { return height; }
12     public void setHeight(int height) { this.height = height; }
13
14     // ... more functionality here
15 }

Code Sample 1: First draft of MyRectangle

What we want the JavaFX script to look like, is this:

 1 import javafx.stage.Stage;
 2 import javafx.scene.Scene;
 3 import javafx.scene.shape.Rectangle;
 4
 5 Stage {
 6     scene: Scene {
 7     // ... more attributes here
 8         content: Rectangle {
 9             x: bind r.x
10             y: bind r.y
11             width: bind r.width
12             height: bind r.height
13         }
14     }
15 }

Code Sample 2: JavaFX program binding the rectangle

This looks simple enough and the only question remaining is: what is “r”? To allow the script to bind to the fields of the Java object, we need to introduce an adapter between the two, which provides a more JavaFX-like interface of the Java class and which will take care of the synchronization. A simplified class-diagram of the three objects is shown in Figure 1:

Class Diagram

Class Diagram

The basis interface of the adapter can be defined as follows:

 1 public class RectangleAdapter {
 2     public-read var x: Integer;
 3     public-read var y: Integer;
 4     public-read var width: Integer;
 5     public-read var height: Integer;
 6
 7     public-init var javaRectangle: MyRectangle;
 8 }

Code Sample 3: First draft of the adapter RectangleAdapter

Only thing left to do is to add the following line to the script in Listing 3. This will create a new instance of RectangleAdapter and its adaptee Rectangle.

var r = RectangleAdapter { javaRectangle: new MyRectangle }
Looks like we are about halfway through. We defined an adapter and connected it to the JavaFX class. Now let's take a look at the second part: connecting the adapter with the Java object.

Synchronizing with the Java object

Synchronizing the adapter whenever the state of the Java object changes requires some more work. In general there are two possibilities to implement this functionality. The first solution is to make the adapter an observer (or "listener" to speak more Java-like) of the Java object. This is usually the preferred approach, unfortunately it is not always possible. For example, if you are dealing with existing classes, which you cannot change, this approach would be problematic. The second solution, which does not require any changes to the Java class, is to request the current state repeatedly.
Synchronizing using the observer-pattern
There are numerous ways to implement the observer-pattern. Since this article is not so much about implementing Java programs, I will use the simplest approach by extending Observable. This is a utility-class, which provides all of the functionality required by the observer-pattern. All we need to do is add the code to initiate the notification of our observers. The class Observable requires two method-calls for that: setChanged() to signal the object has changed and notifyObservers() to start the notification-process. In our code example, I added a small private method startNotification() to bundle both calls and updated the setters. Code sample 4 shows the new version of the class Rectangle.
 1 import java.util.Observable;
 2
 3 public class MyRectangle extends Observable {
 4     private int x, y;
 5     private int width, height;
 6
 7     public int getX() { return x; }
 8     public void setX(int x) {
 9         this.x = x;
10         startNotification();
11     }
12
13     public int getY() { return y; }
14     public void setY(int y) {
15         this.y = y;
16         startNotification();
17     }
18
19     public int getWidth() { return width; }
20     public void setWidth(int width) {
21         this.width = width;
22         startNotification();
23     }
24
25     public int getHeight() { return height; }
26     public void setHeight(int height) {
27         this.height = height;
28         startNotification();
29     }
30     private void startNotification() {
31         setChanged();
32         notifyObservers();
33     }
34
35     // ... more functionality here
36 }

Code Sample 4: MyRectangle implementing Observable

To finish this solution, we need our adapter to extend the interface Observer. That requires to implement its method update(), and to register the adapter. The registration is done by calling addObserver() in the onReplace-trigger of the attribute javaRectangle.

The method update() comes with two parameters, which are helpful, if we want to observe more than one object or if we are dealing with different events. But in our code example, we can ignore them. Only thing left to do is pull the new values from javaRectangle and update the attributes of the adapter.

But wait! You have to be careful here. While our Java code can run on any thread, JavaFX Script is picky here. We need to make sure, that the update is done on the right thread, otherwise the bindings and subsequent actions might fail.

JavaFX Script provides a mechanism for this. The method FX.deferAction() takes a simple function as a parameter and makes sure, that it is executed on the right thread. The complete adapter is shown in code sample 5.

 1 import java.lang.Object;
 2 import java.util.Observable;
 3 import java.util.Observer;
 4
 5 public class RectangleAdapter extends Observer {
 6     public-read var x: Integer;
 7     public-read var y: Integer;
 8     public-read var width: Integer;
 9     public-read var height: Integer;
10
11     public-init var javaRectangle: MyRectangle
12         on replace { javaRectangle.addObserver(this); }
13
14     override function update(observable: Observable, arg: Object) {
15         FX.deferAction(
16             function(): Void {
17                 x = javaRectangle.getX();
18                 y = javaRectangle.getY();
19                 height = javaRectangle.getHeight();
20                 width = javaRectangle.getWidth();
21             }
22         );
23     }
24 }

Code Sample 5: RectangleAdapter implementing Observer

Synchronizing by pulling the state

The second solution does not require any change to the Java class. Let's assume, we do not want to bind to an object of our own Rectangle, but to an instance of java.awt.Rectangle. This class does not provide any mechanisms to observe state-changes. Instead we have to pull the current state on a regular base to notice changes.

The JavaFX class PauseTransition provides exactly the needed functionality. It comes with two main attributes to setup the behavior. The parameter action defines the task, which needs to be executed, and the parameter duration defines the gap between two consecutive calls.

Defining the right value for the duration is probably the toughest part. The best choice depends on the context. If the value is too low, precious CPU cycles are wasted, if the value is too high, the update might come too late. In our example, the duration is set to 10 ms, which ensures smooth animations.

The adapter for the second solution is shown in code sample 6. Note, that we do not have deal with threading in this solution, because the action of the ActionTransition is already executed on the right thread.

 1 import java.awt.Rectangle;
 2 import javafx.animation.Timeline;
 3 import javafx.animation.transition.PauseTransition;
 4
 5 public class RectangleAdapter {
 6     public-read var x: Integer;
 7     public-read var y: Integer;
 8     public-read var width: Integer;
 9     public-read var height: Integer;
10
11     var action: PauseTransition;
12     public-init var javaRectangle: Rectangle
13         on replace {
14             action = PauseTransition {
15                 duration: 10ms
16                 action: function(): Void {
17                     x = javaRectangle.x;
18                     y = javaRectangle.y;
19                     height = javaRectangle.height;
20                     width = javaRectangle.width;
21                 }
22                 repeatCount: Timeline.INDEFINITE;
23             }
24             action.play();
25         }
26
27     public function stop() {
28         action.stop();
29     }
30 }

Code Sample 6: RectangleAdapter pulling the status

Summary

This article presented two solutions to implement a simple binding of a Java object. The first solution is always preferable, because it uses CPU cycles only when an update took place, while the second solution burns cycles even when nothing happens. The second solution is a fall back in cases, where the observer pattern cannot be implemented.

JavaFX is out – or thinking about my last year with JavaFX

It’s been done. Today the first version of the JavaFX SDK was released. What better occasion could there be to reactivate my blog? And it is a good time to stop for a moment and think about the past year in which I have worked on JavaFX.

I started to learn JavaFX (or was it still called F3 at that time?) a little more than a year ago, mostly in my spare time. I liked the ideas of the language and enjoyed playing with that. When my manager asked for volunteers to help the JavaFX compiler team a couple of weeks later, there was certainly nothing to stop me and so I joined the probably most professional and most experienced team, I have ever worked with.

The beginning was a lot of fun. The language had changed quite a lot compared to the interpreted version, which I was used to, but there was no documentation at all. So I had to learn JavaFX Script the hard way, by studying the grammar-file. Ever tried to learn a language by looking at the grammar? It’s definitely something one should try. 😉

The first weeks were probably the most exciting once. The syntax of the language changed almost every week, there were zillions of discussions on our mail-alias going on at the same time about different aspects of the language, and there were only very few of us, but all of us dedicated and devoted to this new thing called JavaFX.

Personally, it was amazing how much I was learning. For example, in the past, I always thought, it’s quite freaky, if C/C++ – developers were looking at the code, which was generated by the compiler. They should focus on the high-level language, right? That’s why it was invented, wasn’t it? And here I was: not only looking at the Java code, which was generated by the JavaFX compiler, but also at the bytecode, which was generated from Java code…

And then there was JavaOne. All of us had worked hard, especially during the weeks immediately prior to JavaOne, and we were excited to see how people would react. One needs to know, that JavaFX was still questioned by a lot of people at that time. More than once, I had conversations with my colleagues like this: “Is JavaFX already dead?” – “No, wait until JavaOne and you will see…” – “If it dies slowly or fast?” 🙂

As we all know by now, JavaOne was a major success for JavaFX. We presented our prototypes on different devices, showed cool new features, and most importantly people became interested. And everything changed. Almost over night, JavaFX became one of the biggest efforts at Sun – and a prestigious one, too. Suddenly people from everywhere were joining the effort, mostly managers and architects though, not so many code-monkeys like myself. 😉 All over the place new projects were started, processes and committees were established… For better or for worse, the effort grew tremendously right after JavaOne.

The last couple of months were dedicated mostly to finish our work for the 1.0 release: implementing the last missing pieces, fixing bugs, improving the performance… The beta release was pretty unspectacular IMO and we went back to our daily routine pretty quickly. But now the 1.0 version is out, and once again I am very excited to see, what people think about the product.

Oh, yes, and before I forget, here is the link, were you can download the JavaFX SDK and see a whole bunch of samples with source-code. Have fun and tell me what you think! 🙂

Creating JavaFX sequences in Java programs

The last article explained, how sequences are represented in the runtime and touched some of the possibilities to create new JavaFX sequences in Java programs. This article and the follow-up will focus on sequence-creation and give an overview of additional possibilities.

The helper-class Sequences

The helper class Sequences in the package com.sun.javafx.runtime.sequence provides a number of methods to create sequences. Most of them have also been presented in the last article.

The methods can be divided in two groups. The first group are distinct factory methods. They create a new sequence from some kind of elements-list:

  • make() – Creates a sequence from a java.util.List, a part of an array or from explicitly listed elements.
  • fromArray() – Creates a sequence from the elements of an array.
  • fromCollection – Creates a new sequence with the elements of a given Collection. Since the elements of a Collection are unordered there is no guarantee about the order in the sequence.
  • range / rangeExclusive() – Creates sequences, which represent Integer- or Number-intervals.
  • singleton – Creates a sequence with a single element.
  • emptySequence() – Creates an empty sequence.

The second group alters an existing sequence. As sequences are implemented immutable, a new sequence is created and returned.

  • concatenate() – Creates a new sequence by concatenating sequences.
  • filter() – Creates a new sequence by masking out some of the elements of a sequence.
  • reverse – Creates a new sequence by reversing the order of a sequence.
  • subsequence – Creates a sequence, which is a fragment of a sequence.
  • upcast – Creates a new sequence by upcasting the elements of a sequence.
  • map – Creates a sequence which one would get after applying a mapping-function to the elements of a sequence. The mapping-function must be defined by implementing the interface SequenceMapper, which is also part of the package com.sun.javafx.runtime.sequence.
  • sort – Creates a new sequence by sorting the elements of a given sequence. The order can be induced either by the natural order of the elements, if they implement java.util.Comparable, or by providing a java.util.Comparator of the elements.

The class SequenceBuilder

The SequenceBuilder in the package com.sun.javafx.runtime.sequence provides a convenient way to create sequences incrementally. It is similar to the class StringBuilder, which creates Strings.

An instance of SequenceBuilder can only create one sequence at a time. It starts with an empty sequence to which elements are appended by calling one of its add()-methods. The result can be extracted by calling the method toSequence().

Internal design of JavaFX sequences

This is the first part in a series of articles about JavaFX sequences. It will focus on the basic concepts of the implementation and explain some of the possibilities how to create JavaFX sequences in Java programs.

The first and foremost concept one needs to know in order to understand the implementation of JavaFX sequences is, that they are immutable. This is surprising, considering how often sequences are changed. But immutable objects have a number of advantages. If you want to know more about them, I recommend this article. After reading the article, it would have been a bigger surprise, if Brian, the author, had not implemented sequences as immutable objects. :-)

But inspite the many advantages of immutable objects, it is not obvious to come up with an implementation for a general purpose data-structure like JavaFX sequences. In this article I will provide an overview on how sequences are implemented. This will not only give a better understanding of the internals of JavaFX, but it may also serve as a good example, if one wants to design an immutable data-structure oneself.

Overview

When a sequence is changed, a new sequence-object needs to be created. The trick is not to copy the elements of the original sequence to the new object, but to store a reference together with an implementation of the change. For example if two sequences need to be concatenated, the new sequence stores references to the original sequences and resolves requests by calculating the result of the concatenation on the fly.

The highlevel-design of sequences is as follows. An interface defines the general behavior of a sequence. The implementations can be divided into two categories. There are the atomic sequence-types, which build the foundation of any sequence, and the derived sequences-types, which take one (or more) sequences and implement exactly one operation on them.

There are some more classes which are essential when dealing with sequences. On of them is the class Sequences, which is mentioned a couple of times in this article. Among others, it contains a number of useful methods to create and change sequences. All of these classes are located in the package com.sun.javafx.runtime.sequence, which can be found in the openjfx-repository and which are part of the JavaFX runtime.

Atomic Sequence Types

Atomic sequence types do not refer to any other sequence, but hold all information themselves. Every sequence is based on one or more atomic sequence.

EmptySequence

An empty sequence contains no element, but nevertheless it stores the class of the elements. That’s because an empty sequence of class A-elements is different from an empty sequence of class B-elements. The implementation of empty sequences takes advantage of the fact that they are immutable to save memory. For every class only one empty sequence gets created. Any subsequent call of the factory-method returns a reference to that sequence.

An empty sequence can be created in Java code by calling Sequences.emptySequence() with the class of the sequence-elements as parameter.

SingletonSequence

The name of this class might be misleading, because it has nothing to do with the Singleton pattern, as one might associate. A SingletonSequence is simply a sequence, which contains only one element.

A sequence with exactly one element can be created in Java code by calling Sequences.singleton() with the class of the sequence-elements and the single element as parameter.

ArraySequence

An ArraySequence is the most flexible atomic sequence type. Its values are backed by an array, thus allowing an arbitrary sequence of elements. But remember, null-values are not allowed in a sequence. This is certainly true for an ArraySequence as well, therefore the underlying array is not allowed to contain null-values.

There are a number of factory-methods to create arbitrary atomic sequences, mainly Sequences.make() with different parameter-sets (e.g. varargs, List) and Sequences.fromArray() to create sequences directly from arrays.

IntRangeSequence / NumberRangeSequence

The classes IntRangeSequence and NumberRangeSequence are used to represent number intervals. They are defined by a start-value, the size of the sequence and a step-size. The content of these sequences are calculated on request.

Range-sequences can be created with Sequences.range() or Sequences.rangeExclusive(). Parameters for these factory-methods are the lower and upper bounds as well as the optional step-size. The type of the created sequence depends on the type of the input-parameters. If all parameters are integers, the result is an IntRangeSequence, and in all other cases the result is a NumberRangeSequence. The method range() creates a sequence with the upper bound included, while rangeExclusive creates a sequence without the upper bound.

Derived Sequence Types

A derived sequence is created, when an existing sequence is altered. A derived sequence stores a reference to the original sequence(s) and implements methods to calculate the changes on-the-fly. The referenced sequences are accessed by the interface Sequence, thus they can be any type of sequence, atomic or derived.

Concepts of derived sequences

Figure 1: Concepts of derived sequences a) CompositeSequence, b) SubSequence, c) ReverseSequence, and d) FilterSequence

CompositeSequence

A CompositeSequence implements the concatenation of an arbitrary number of sequences. To get an element of the concatenated sequence, first the right subsequence needs to be selected and after that the element can be requested from that subsequence. Figure 1a) shows this process for a sequence which is the concatenation of three subsequences. First the CompositeSequence determines the second sequence as the one containing position x and after that the element is requested from that sequence.

Sequences can be concatenated in Java code by calling the method Sequences.concatenate().

SubSequence

A SubSequence is a part of a sequence. The implementation is straightforward, the bounds are stored and used to get the elements of the subsequence. Figure 1b) shows how the elements of a SubSequence are requested from the underlying sequence.

The subsequence of a sequence can be evaluated in Java code by calling Sequences.subsequence() or by calling the method subsequence() of the sequence directly.

ReplacementSequence

A ReplacementSequence replaces one element of the underlying sequence. The position and the value of the new element are stored. The getter compares the requested position with the position of that element and returns the stored value, if they match. Otherwise the element is requested from the underlying sequence.

A ReplacementSequence is usually not created directly but it is heavily used by the SequenceMutator, which I will cover in another article of this series.

ReverseSequence

A ReverseSequence is the reversal of a sequence. The elements are not copied in reverse order to create the new sequence, but instead the getter of a ReverseSequence recalculates the position to request the elements from the original sequence. So when the first element of the ReverseSequence is requested, the last element of the original sequence is returned, if the second element is requested, the last but one is returned etc. Figure 1c) shows the reversal of the sequence by recalculating positions.

The reversal of a sequence can be calculated in Java code by calling Sequences.reverse() or by calling the method reverse() of the sequence directly.

UpcastSequence

A sequence can be casted to a sequence, which elements are supertypes of the original sequence’s elements. An UpcastSequence is taking care of that by upcasting the requested elements in the getter.

Upcasting of a sequence can be achieved in Java code by calling Sequences.upcast().

FilterSequence

A FilterSequence applies a filter in form of a bitfield on a sequence. The bitfield defines which elements are visible and which ones are ignored. To simplify the usage, an array to store the positions of the visible elements is created during initialization. Figure 1d) shows an example. The first and third elements are visible, while the second element is not. Therefore the created array contains the first and third posit
ion, omitting the second one.

To filter a sequence in Java code the method Sequences.filter() can be used.

Conclusion

This article, presented the basic concepts of the implementation for JavaFX sequences. The foundation of a sequence are always one or more atomic sequences. Since sequences are implemented immutable, derived sequences were introduced to implement changes on sequences. A derived sequence implements exactly one operation on one or more sequences.

If a sequence is altered again and again, a tree of derived sequences
is created to represent those changes. As one can imagine, this can
easily result in inefficient structures, therefore optimizations are going to be introduced to increase performance. This is an ongoing process and currently under discussion, therefore I did not cover it in this article.

The next article will cover some more possibilities to create and alter sequences in Java code by introducing the classes SequenceBuilder and SequenceMutator.

Refactoring a function to become a bound function

Bound functions are one of the most powerful features, that JavaFX Script introduces, but probably also one of the hardest concepts to understand. There are two main use-cases for bound functions:

  1. The result of the function depends on some external attributes.
  2. The functions returns an object, which is created in the function. If one of the dependencies changes, the function should not create a new object, but alter the one which was created in the first call.

While I have not encountered the second case so far (but maybe that’s because the concept of bound functions has not fully been absorbed by developers like me). The first case is very common. Consider for example the class in Code Sample 1. (Warning: this is a real world example taken from the current UI-runtime.) 🙂

 1 public class CanvasElement {
 2     public attribute parentCanvasElement: CanvasElement;
 3     
 4     public function getContainer(): Container {
 5         while (p <> null) {
 6             if (p instanceof Container) {
 7                 return p as Container;
 8             }
 9             p = p.parentCanvasElement;
10         }
11         return null;
12     }
13 }

Code Sample 1: The original function getContainer()

The class CanvasElement is a node in a tree. The function getContainer() returns the closest parent, which is a Container. The result of the function depends on the attribute parentCanvasElement. Evidently, if the node is moved to another place in the tree, the function will return another result. Now, wouldn’t it be nice to make this function a bound function? This would allow us to notify any object that needs to be notified about the new parentContainer without writing a single line of code.

Unfortunately the permitted statements in a bound function are quite strict. Only the definitions of local variables are allowed plus a final expression to return the result of the function. In other words only method-bodies of the following form are allowed:

1 bound function foo() {  
2     var i = ...; 
3     var j = ...; 
4     [...] 
5     return result; 
6 }

Code Sample 2: The required format of bound functions (informal)

This looks pretty strict at first sight. Consider the function in Code Sample 1. Rewriting the function to match this pattern directly will be hard, if not impossible. But here is a little recipe with which every function can be turned into a legal bound function.

  1. Take the function-body and create a private function with all dependencies as parameters. (I usually make this function static, so that the compiler warns me, if I forgot a non-static-dependency.)
  2. Make the original function a bound function and call the function just created, passing all dependencies to it.

By applying this recipe, the bound function follows the allowed syntax. And whenever a dependency changes the result gets reevaluated. Voilá!

Code Sample 3 shows the refactored function getContainer() from the example.

 1 public class CanvasElement {
 2     public attribute parentCanvasElement: CanvasElement;
 3     
 4     public bound function getContainer(): Container {
 5         return getParentContainer(parentCanvasElement);
 6     }
 7 
 8     private static function getParentContainer(p: CanvasElement): Container {
 9         while (p <> null) {
10             if (p instanceof Container) {
11                 return p as Container;
12             }
13             p = p.parentCanvasElement;
14         }
15         return null;
16     }
17 }

Code Sample 3: The refactored and bound function getComponent()

Creating JavaFX objects in Java programs

So far, all of the examples in my previous article about how to use JavaFX objects in Java code expected the object as an input parameter. But what if you want to create a JavaFX object directly? In this article, I will describe a very simple but effective solution.

(Edit: As Mike Azzi pointed out to me (thanks again!), I should have mention, that it is also possible to create JavaFX objects by using the compiled Java classes directly. I plan to cover this later, in a series of articles about what happens during compilation.)

Let’s assume we want to create an instance of MyJavaFXClass as it was defined in the previous article. Code sample 1 shows the implementation of the class.

 1 import java.lang.System;
 2 
 3 public class MyJavaFXClass {
 4 
 5     public attribute property: String;
 6 
 7     public function printProperty() {
 8         System.out.println (property);
 9     }
10 }

Code Sample 1: Definition of MyJavaFXClass

To create an instance of this class in JavaFX Script, we would usually define an object literal. We can do that as well in Java and pass the object literal to the JavaFX-script engine we already encountered in the previous article. The script engine can parse the object literal and return a new instance of the class.

Code sample 2 shows a Java class, which creates an instance of MyJavaFXClass and uses the script engine to call its function printProperty.

 1 import javax.script.ScriptEngineManager;
 2 import com.sun.javafx.api.JavaFXScriptEngine;
 3 
 4 public class Main {
 5 
 6     public static void main (String[] args) {
 7         ScriptEngineManager manager = new ScriptEngineManager();
 8         JavaFXScriptEngine fxEngine = 
 9                 (JavaFXScriptEngine) manager.getEngineByName ("javafx");
10 
11         try {
12             Object o = fxEngine.eval
13                     ("MyJavaFXClass { property: \"JavaFX class created in Java\" }");
14             fxEngine.invokeMethod (o, "printProperty");
15         } catch (Exception ex) {
16             ex.printStackTrace();
17         }       
18     }
19 }

Code Sample 2: Constructing MyJavaFXClass in Java program

Using JavaFX sequences in a Java program

The last two articles were about how to pass and deal with primitive datatypes, Java objects, and JavaFX objects. This article focuses on JavaFX sequences.

There are quite a number of Java classes implemented to represent JavaFX sequences. Which implementation is used depends on the input parameters during creation. All of the implementations share the interface Sequence, which we must therefore use when dealing with sequences in Java code.

Using a specific sequence

The interface Sequence is generic. When using it as a parameter in a method, we can define the exact type in the method signature or we leave the type open by defining a generic method. The first code sample shows a method which expects a sequence of Integer-objects. Note that the type is not exactly defined, but used as an upper bound. This needs to be done always.

 1 import com.sun.javafx.runtime.sequence.Sequence;
 2 import java.util.Iterator;
 3 
 4 public class MyIntegerLibrary {
 5 
 6     public static void printIntegerSum (Sequence<? extends Integer> seq) {
 7         int sum = 0;
 8         for (Iterator<? extends Integer> it = seq.iterator(); it.hasNext();) {
 9             sum += it.next();
10         }
11         System.out.println(sum);
12     }
13 }

Code Sample 1: Calculating sum of an Integer sequence

The method printIntegerSum in Code Sample 1 iterates through the sequence of Integers and calculates the sum. The JavaFX statement in Code Sample 2 creates a sequence of 5 integers and calls the method printIntegerSum.

 1 MyIntegerLibrary.printIntegerSum( [2, 3, 5, 7, 11] );

Code Sample 2: Calling printIntegerSum

Note that the type of this example is pretty strict. It is for example not possible to pass a sequence of Byte-objects. If you want to allow a more general sequence, you have to use a more general type. The sequence cannot be cast automatically. In the example above, the class Number can be used to allow any kind numbers in the given sequence.

Using a generic sequence

Using the interface Sequence in generic methods works as one would expect (with one exception, which will be covered later). Code Sample 3 defines a method which takes an arbitrary sequence and prints its elements using the method toString().

 1 import com.sun.javafx.runtime.sequence.Sequence;
 2 import java.util.Iterator;
 3 
 4 public class MySequencePrinter {
 5 
 6     public static <T> void printSequence(Sequence<T> seq) {
 7         for (Iterator<T> it = seq.iterator(); it.hasNext();) {
 8             System.out.println(it.next().toString());
 9         }
10     } 
11 }

Code Sample 3: Printing the elements of an arbitrary sequence

The following statement calls the method with an Integer-Sequence.

 1 MySequencePrinter.printSequence( [2, 3, 5, 7, 11] );

Code Sample 4: Calling the Sequence-Printer

When defining a method which expects a sequence and a single element of the same type, you have to define the type of the sequence bounded as in the first example. Code Sample 5 defines a generic method, which takes an arbitrary sequence and an element. As you can see, the type of the sequence-elements extend the type of the single element.

 1 import com.sun.javafx.runtime.sequence.Sequence;
 2 import java.util.Iterator;
 3 
 4 public class MyElementCounter {
 5     
 6     public static <T> void countElement(Sequence<? extends T> seq, T key) {
 7         int sum = 0;
 8         for (Iterator<? extends T>it = seq.iterator(); it.hasNext();) {
 9             if (key.equals(it.next()))
10                 ++sum;
11         }
12         System.out.println(sum);
13     }
14 }

Code Sample 5: Counting element in a sequence

The method countElement in Code Sample 5 counts how often the element appears in the sequence. The following statement calls the method.

 1 MyElementCounter.countElement( [2, 3, 2, 2, 5, 7, 11, 2], 2 );

Code Sample 6: Calling counting element

If you want to know more about generic types and generric methods, this chapter of the Java Tutorial is a good start.

This article finishes the small series about how to deal with parameters passed from JavaFX to Java after primitive datatypes and Java objects were covered in the first article and JavaFX objects in the second article. The next article will be a short intermezzo on how to create JavaFX objects in Java code. After that will follow another small series which focuses some more on sequences.

Using JavaFX objects in Java code

As shown in the last article, passing parameters between a JavaFX script and Java code is fairly simple, if the parameters are native to the Java language. But if we want to pass a JavaFX-object, things are different. The main obstacle is the fact, that the Java compiler is not able to read JavaFX scripts directly to extract the needed information. But JavaFX objects can be shared between a JavaFX and Java code by using one of these approaches:

  1. Java interfaces
  2. JSR 223 – Scripting for the Java Platform
  3. com.sun.tools.javafx.ui.FXBean
  4. Using the Java classes created by the JavaFX compiler directly
  5. (JavaFX Reflection)

The last two approaches are mentioned just for completeness. It requires some detailed knowledge of how a JavaFX class is translated into Java code to use the classes directly and it is more cumbersome than the other approaches. For these reasons I’ll skip it in this article, but plan to dedicate an entire article on an overview of how JavaFX classes get translated to Java code. JavaFX Reflection is not implemented yet, but planned. I will add a description for this approach as soon as the details are decided.

Code sample 1 shows a class, which needs to be passed to Java. It has a public function printProperty, which we want to call from Java code.

import java.lang.System;

public class MyJavaFXClass {
    public attribute property: String;
    public function printProperty() {
        System.out.println(property);
    }
}

Code Sample 1: Definition of MyJavaFXClass

Java interfaces

The first approach (and my favorite one) is to use Java interfaces for passing JavaFX classes to Java code. This approach requires to define a Java interface, let the JavaFX-class implement the interface and reference the interface in the Java code. For our little example, we define the interface Printable which is shown in Code Sample 2.

public interface Printable {
    void printProperty();
}

Code Sample 2: The interface Printable

Now we can write a Java class, which takes an implementation of the interface Printable and calls its method printProperty (code sample 3).

public class MyLibraryByInterface {
    public static void print (Printable p) {
        p.printProperty();
    }
}

Code Sample 3: Using an interface to access JavaFX code

The last step is to modify the JavaFX class from code sample 1, so it implements the interface Printable. Also I added some code to create an instance of this class and pass it to the Java library.

import java.lang.System;

public class MyJavaFXClass extends Printable {
    public attribute property: String;
    public function printProperty() {
        System.out.println(property);
    }
}

var o = MyJavaFXClass {
    property: "Hello JavaFX by Interface"
};

MyLibraryByInterface.print(o);

Code Sample 4: Implementing a Java interface with JavaFX Script

This approach has some major advantages. As you can see, the Java code contains no JavaFX-specific code at all. The very same code could handle a Java-object which implements the interface Printable. As a side effect, you can easily write Java-code which can be used with JavaFX-code and Java-code at the same time. Or if you want to migrate parts of an existing Java-application, you can simply switch to use interfaces and then exchange class after class. (Well, it’s probably not going to be THAT simple, but you get the idea.) 🙂
The drawback of this approach is, that one cannot instantiate objects directly as usual when using interfaces. You either need to pass the objects to the Java code or use factory-methods. But the very first factory still needs to be passed to the Java code – or created with the next approach.

JSR 223 – Scripting for the Java Platform

JSR 223 – Scripting for the Java Platform defines mechanisms to access scripting languages from Java code. An implementation for JavaFX Script is available and part of the JavaFX Runtime. With the provided scripting engine, you can read JavaFX-files, compile, and run them. And because the engine for JavaFX Script implements the interface Invocable, it is even possible to call members of JavaFX objects.

In code sample 5, a class is defined, which takes an arbitrary object and tries to call its method printProperty with the help of the JavaFX Scripting Engine. The first two lines of the method get the ScriptingEngineManager, which manages the available scripting engines. The engine for JavaFX Script is requested and casted to the interface Invocable. The line in the try-block invokes the function printproperty of the object that was given as parameter.

import javax.script.*;

public class MyLibraryByScriptEngine {
    public static void print (Object o) {
        ScriptEngineManager manager = new ScriptEngineManager();
        Invocable fxEngine = (Invocable) manager.getEngineByName("javafx");
        try {
            fxEngine.invokeMethod(o, "printProperty");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Code sample 5: Using the JavaFX Script engine

For this approach we do not need to change the JavaFX class at all. The following two lines will create the object and pass it to the Java library.

import MyJavaFXClass;

var o = MyJavaFXClass {
    property: "Hello JavaFX by ScriptEngine"
};

MyLibraryByScriptEngine.print(o);

Code Sample 6: Calling MyLibraryByScriptEngine

As you can see, the Java code to handle a JavaFX object is slighty more complicated than in the first approach. The major drawback is, that the compiler is not able to check the code. Instead you will receive a runtime exception if for example you have a typing error in the method-name printProperty, like I did. 😉

But on the other hand this approach is the most powerful one of these three aproaches, as you are able to access JavaFX-files directly. We have just scratched the possibilities here; if you want to read more, I recommend this general introduction or this article specific to JavaFX Script.

com.sun.tools.javafx.ui.FXBean

The last approach, to use the class FXBean within the JavaFX Runtime, differs from the other approaches, because it does not give you access to the functions of a JavaFX object but to its attributes. The code sample 7 receives an arbitrary object, reads its attribute property with the help of the class FXBean, and prints the attribute.

First an FXBean is constructed using the class of the given object as parameter. The next line requests an attribute with the name property and the type string. Actually the FXBean does not return a String immeadiately, but an ObjectVariable<String>. This is the internal representation of a String-attribute mapped to Java code. We get the value of the attribute by calling the method get, which is printed in the last line of the try-block.

import com.sun.tools.javafx.ui.FXBean;
import com.sun.javafx.runtime.location.ObjectVariable;

public class MyLibraryByFXBean {
    public static void print (Object o) {
        try {
            FXBean bean = new FXBean(o.getClass());
            ObjectVariable<String> attribute =
                    (ObjectVariable<String>)bean.getObject(o, "property");
            String property = attribute.get();
            System.out.println(property);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Code Sample 7: Using the class FXBean

The next two lines of JavaFX code create an object and pass it to the Java library.

import MyJavaFXClass;

var o = MyJavaFXClass {
    property: "Hello JavaFX by FXBean"
};

MyLibraryByFXBean.print(o);

Code Sample 8: Calling MyLibraryByFXBean

As already mentioned above does this approach differ from the others and can therefore not be compared. Using the FXBean you are able to read and modify attributes directly. There are a number of methods to simplify acces to attributes of type Number, Integer, Boolean, and sequences.

After considering the primitive datatypes and Java objects in the last article, we took a look at JavaFX objects in this article. The next article will focus on dealing with sequences in Java code.

Writing Java code for JavaFX Script

When writing Java classes which can be used in JavaFX scripts, three factors have to be considered: how to create a Java object, how to access its members and how to pass parameters between Java and JavaFX.

Creating Java Objects

Creating Java objects from a JavaFX script is fairly simple, as this functionality is already built into the language. To setup a class, which can be instantiated in a JavaFX script, you just have to provide constructors as you would for the usage in Java classes. In Code Sample 1, a class with two constructors is created.

public class MyJavaClass {

    private String property;

    public MyJavaClass() {
        this.property = "Hello World";
    };

    public MyJavaClass(String property) {
         this.property = property;
    };

    public void printProperty() {
        System.out.println(property);
    }
}

Code Sample 1: Definition of MyJavaClass

To create an instance of MyJavaClass in a JavaFX script, simply call its constructor. Notify that the brackets can be omitted, if a constructor without parameters is called.

var o1 = new MyJavaClass;
var o2 = new MyJavaClass("Goodbye World!");

Code Sample 2: Instantiating MyJavaClass in JavaFX Script

Accessing Members

Accessing members of Java objects from a JavaFX script works as it would in a pure Java environment. The access modifiers public, protected and private imply the same restrictions. As you will see in the example about passing parameters, you can also access static members defined in your Java classes. The following lines create an instance of MyJavaClass and call its method printProperty.

var o1 = new MyJavaClass("Goodbye World!");
o1.printProperty();

Code Sample 3: Accessing a member of MyJavaClass in JavaFX Script

Passing Parameters

When it comes to passing parameters, there are four categories of data types to consider. They are listed in the following table.

JavaFX Type Java Type
Number double / java.lang.Double
Integer int / java.lang.Integer
Boolean boolean / java.lang.Boolean
String java.lang.String
Java objects
JavaFX objects
Sequences

Passing primitive data types and Java objects is straightforward. The primitive data types Number, Integer, Boolean, and String are mapped to primitive data types or the equivalent objects as shown in the table. In Code Sample 4, a Java class is defined with methods to take these data types and return them.

public class MyLibrary {

    public static double returnDoublePrimitive (double d) {
        return d;
    }
    public static Double returnDoubleObject (Double d) {
        return d; 
    }

    public static int returnIntegerPrimitive (int i)     {
        return i;
    }
    public static Integer returnIntegerObject  (Integer i) {
        return i;
    }

    public static boolean returnBooleanPrimitive (boolean b) {
        return b;
    }
    public static Boolean returnBooleanObject (Boolean b) {
        return b;
    }

    public static String returnString (String s) {
        return s;
    }
}

Code Sample 4: Definition of MyLibrary

The JavaFX script in Code Sample 5 calls the Java-methods. As the code sample shows, it makes no difference to the JavaFX script, if a primitive data type or the corresponding object is needed as a parameter.

var n1: Number = 3.1415;
var n2: Number;
n2 = MyLibrary.returnDoublePrimitive(n1);
n2 = MyLibrary.returnDoubleObject(n1);

var i1: Integer = 42;
var i2: Integer;
i2 = MyLibrary.returnIntegerPrimitive(i1);
i2 = MyLibrary.returnIntegerObject(i1);

var b1: Boolean = true;
var b2: Boolean;
b2 = MyLibrary.returnBooleanPrimitive(b1);
b2 = MyLibrary.returnBooleanObject(b1);

var s1: String = "Passing a String";
var s2: String;
s2 = MyLibrary.returnString(s1);

Code Sample 5: Passing primitive JavaFX data types

When defining a new Java interface, I’d recommended to use only the data types, which are defined in JavaFX. If that is not possible, the data has to be cast. In most cases, this is done implicitly, but in some cases it has to be done explicitly using the methods byteValue(), shortValue(), longValue(), and floatValue(). To pass a char, the method charAt() of the data type String can be used. In Code Sample 6, a Java class is defined which expects these data types.

There also exist two methods doubleValue() and intValue(), which can be used to cast a Number to an Integer and vice versa. These methods can also be used with literals directly, e.g. “42.floatValue()” casts the magic number to a float.

public class MyLibraryCast {

    public static byte returnBytePrimitive (byte b) {
        return b;
    }
    public static Byte returnByteObject    (Byte b) {
        return b;
    }

    public static short returnShortPrimitive (short s) {
        return s;
    }
    public static Short returnShortObject    (Short s) {
        return s;
    }

    public static long returnLongPrimitive (long l) {
        return l;
    }
    public static Long returnLongObject    (Long l) {
        return l;
    }

    public static float returnFloatPrimitive (float f) {
        return f;
    }
    public static Float returnFloatObject    (Float f) {
        return f;
    }

    public static char returnCharacterPrimitive   (char c) {
        return c;
    }
    public static Character returnCharacterObject (Character c) {
        return c;
    }
}

Code Sample 6: Definition of MyLibraryCast

Code sample 7 shows how these methods can be called. In these cases there is a difference between using the primitive data types in Java and using the equivalent objects as you can see in the method-calls below.

var i1: Integer = 42;
var i2: Integer;

i2 = MyLibraryCast.returnBytePrimitive(i1);
i2 = MyLibraryCast.returnByteObject(i1.byteValue());

i2 = MyLibraryCast.returnShortPrimitive(i1);
i2 = MyLibraryCast.returnShortObject(i1.shortValue());

i2 = MyLibraryCast.returnLongPrimitive(i1) as Integer;
i2 = MyLibraryCast.returnLongObject(i1.longValue()).intValue();

var n1: Number = 3.1415;
var n2: Number;

n2 = MyLibraryCast.returnFloatPrimitive(n1.floatValue());
n2 = MyLibraryCast.returnFloatObject(n1.floatValue());

var s1: String = "c";
var s2: String;

s2 = java.lang.Character.toString (MyLibraryCast.returnCharacterPrimitive (s1.charAt(0)) );
s2 = MyLibraryCast.returnCharacterObject(s1.charAt(0)).toString();

Code Sample 7: Passing primitive Java data types by casting

A Java object does not need to be mapped, but is passed as the very same object. In the Java code it makes no difference if the Java object was passed from a JavaFX script or from some other Java code. The static method in Code Sample 8 take an instance of MyJavaClass (see Code Sample 1), calls its method printProperty and returns the object.

public class MyLibraryForObjects {
    public static MyJavaClass printAndReturnMyJavaClass (MyJavaClass o) {
        o.printProperty();
        return o;
    }
}

Code Sample 8: Definition of MyLibraryForObjects

The JavaFX script in Code Sample 9 creates an instance of MyJavaClass, passes it to the method printAndReturnMyJavaClass and saves the result.

var o1: MyJavaClass = new MyJavaClass("Hello Java!");
var o2: MyJavaClass;

o2 = MyLibraryForObjects.printAndReturnMyJavaClass(o1);

Code Sample 9: Passing a Java object

Passing JavaFX objects and sequences to Java is slightly more difficult and will be covered in the next articles.