PEAT Online Course -
Test-Driven Development
using Kotlin (DELUXE EDITION)

  • Theory 'n' practice
  • Test-driven method
  • Acceptance criteria
  • Learn to code mastery
  • Ace that job interview
  • Free updates
AVAILABLE NOW
VIMEO ON-DEMAND

A Domain Specific Language for JavaFX 2.0 and Scala

10 October 2010 1 comment

9 minutes

1967

Hello and Welcome

Two weeks ago, at the JavaOne 2010 Conference in San Francisco, there was an announcement that Oracle will no longer supporting JavaFX Script as a language. In fact, Sun.Oracle will refactor the current implementation into a Java API and framework in the 2.0.

As many have reported (and obviously mis-reported) Oracle made business and technological decisions about the future of JavaFX. In this blog, I already mentioned a conversation at JavaOne 2010 I had with Brian Goetz, where he explained that the biggest issue creating tools around JavaFX Script was expensive, in comparision to the availability of existing tools for Java. Building the even the essential JavaFX Script tooling for the language was too high a barrier to reach in order to bring JavaFX Script up the same high quality standard. Oracle decision to discontinue in-house development of JavaFX Script  was, then, a time-to-market decision and also reflection of the prototypical experience that following the JavaFX Script platform. Sun.Oracle have learnt many lessons in their drive to implement around compiled JavaFX Script.

The most important innovation in the JavaFX Script language is the binding syntax. It looks incredibly simple but imposes so much hard thinking under the surface of the proverbial architectural iceberg. Just like so many instructional pyramid design in software architectures around the world, JavaFX Script language sits on the top of the ice berg, the easy target, the white motionless precipice of mountain, which innocently languishes above the sealine. Of course, everyone knows it is what lies beneath the sea, which makes an iceberg so formidably dangerous to shipping vessels. Sun.Oracle have been building below this sea-level and have been for a few years already. They will continue to do hard to engineering especially the binding and updates of properties in the implementation of JavaFX 2.0.

The advantages on concentrating on the bottom of the iceberg are:

  • Hardware accelerated graphics architecture and pipeline (a unified temporarily Java2D and Prism worlds. Eventually Java 2D / AWT will go away, several years into the future)
  • Presentation of a unified scene graph architecture thorough 2d and 3d views, think of NetBeans rewritten using scenegraph elements.
  • A brand new Prism based applet and JNLP web startable applications, which will be deployable from the get-go
  • Allow JVM language authors and enthusiasts to create DSL in their own favourite programming languages that (re-)use the forthcoming JavaFX for Java 2.0 API

It is this last one item that I concentrate here. Since JavaOne 2010, yours truly has been seriously ruminating over a possible Scala Domain Specific Language for JavaFX 2.0 for Java API, especially knowing that Sun.Oracle will deliver this functionality in 6-8 months as promised. The bad old days of Sun Microsystem loss of focus over a wide range of products of tentative monetisation are truly gone for ever, especially in Larry Ellison’s open Oracle world.

This is my speculative buy in to the JavaFX 2.0 API. I believe Sun.Oracle will choose a variant of Builder Pattern for the Java API, the Named Parameter Idiom, rather than the legacy JavaBeans, setters and getters. Here is the Node.java in my personal opinion:

package uk.co.xenonique.javafx;

/**
 * Research JavaFX for Java API 2.0
 * @author Peter Pilgrim, 26 September 2010
 */
public abstract class Node {

    private boolean blocksMouse;
    private Bounds boundsInLocal = new Bounds();
    private Bounds boundsInParent = new Bounds();
    private Node clip;
    private boolean focused;
    private boolean hover;
    private String id;
    private Bounds layoutBounds = new Bounds();
    private float layoutX;
    private float layoutY;
    private boolean managed;
    private float opacity;
    private boolean pressed;
    private Parent parent;
    private boolean visible;

    public Node() { super(); }

    public boolean isBlocksMouse() {
        return blocksMouse;
    }

    public Node setBlocksMouse(boolean blocksMouse) {
        this.blocksMouse = blocksMouse;
        return this;
    }

    public Bounds getBoundsInLocal() {
        return boundsInLocal;
    }

    public Bounds getBoundsInParent() {
        return boundsInParent;
    }
    ...

    public boolean isFocused() {
        return focused;
    }

    public boolean isHover() {
        return hover;
    }

    public String getId() {
        return id;
    }

    public Node setId(String id) {
        this.id = id;
        return this;
    }

    public Bounds getLayoutBounds() {
        return layoutBounds;
    }

    public float getLayoutX() {
        return layoutX;
    }

    public float getLayoutY() {
        return layoutY;
    }

    public boolean isManaged() {
        return managed;
    }

    public Node setManaged(boolean managed) {
        this.managed = managed;
        return this;
    }

    public float getOpacity() {
        return opacity;
    }

    public Node setOpacity(float opacity) {
        this.opacity = opacity;
        return this;
    }

    public Parent getParent() {
        return parent;
    }

    public Node setParent(Parent parent) {
        this.parent = parent;
        return this;
    }

    public boolean isPressed() {
        return pressed;
    }

    public boolean isVisible() {
        return visible;
    }

    public Node setVisible(boolean visible) {
        this.visible = visible;
        return this;
    }

    ...

    @Override
    public String toString() {
        return "Node{" + "blocksMouse=" + blocksMouse + ", boundsInLocal=" + boundsInLocal + ", boundsInParent=" + boundsInParent + ", clip=" + clip +
                ", focused=" + focused + ", hover=" + hover + ", id=" + id + ", layoutBounds=" + layoutBounds +
                ", layoutX=" + layoutX + ", layoutY=" + layoutY + ", managed=" + managed + ", opacity=" + opacity +
                ", pressed=" + pressed + ", parent=" + parent + ", visible=" + visible + '}';
    }
}

A Node is the base abstract class that represents one scenegraph node in the new Prism architecture  Remember that top of that iceberg, I was just talking about. It is simply a one-to-one between the abstract node and the underlying implementation rendering primitive in your GPU.

Please note, every Node can have a parent, and are there is no node that is not parentable:

package uk.co.xenonique.javafx;

import java.util.*;

/**
 * @author Peter Pilgrim
 */
public abstract class Parent extends Node {

    protected List<Node> children = new ArrayList<Node>();
    protected boolean needsLayout;

    public Parent() {
    }

    public List<Node> getChildren() {
        return children;
    }

    protected Parent setChildren(List<Node> children) {
        this.children = children;
        return this;
    }

...
    @Override
    public String toString() {
        return "Parent{" + "children=" + children + ", needsLayout=" + needsLayout + '}';
    }

}

Parent and Node are abstract classes. Group is the simplest container type of Parent and it is a concrete type. A Group represents a collection of scene graph nodes, which is not the same as a Container (which not shown in this blog article). So here is the Java API version of a Group.

package uk.co.xenonique.javafx;

import java.util.*;

public class Group extends Parent {

    public Group() {
    }

    public List<Node> getContent() {
        return getChildren();
    }

    public void setContent(List<Node> content) {
        this.setContent( content );
    }
    ...
}

The Group is simple implementation of a Parent in a nutshell.

Let us look at the Shape as a Java class, or at the very least the basics of it. A Shape is a type of node that stores additional painting attributes, for example, the fill and stroke colour. In JavaFX SDK 1.3 these properties are actually Paint types, but for the purposes of research, I made them colours.

package uk.co.xenonique.javafx;

public abstract class Shape extends Node {

    protected Color fill   = new Color( 0x999966);
    protected Color stroke = new Color(0xFF9900);
    protected float strokeWidth = 1.0F;

    public Shape() {
        super();
    }

    public Color getFill() {
        return fill;
    }

    public Shape setFill(Color fill) {
        this.fill = fill;
        return this;
    }
     ...
}

Finally, here is the outline of the remaining JavaFX for Java API classes that we need. Namely, they are the Rectangle, Stage and the Scene classes:

package uk.co.xenonique.javafx;

/**
 * @author Peter Pilgrim
 */
public class Rectangle extends Shape {
    private float x;
    private float y;
    private float width;
    private float height;

    public Rectangle() {
    }

    public float getHeight() {
        return height;
    }

    public Rectangle setHeight(float height) {
        this.height = height;
        return this;
    }

    ...
}

The Stage:

package uk.co.xenonique.javafx;

public class Stage {

    private String title;
    private float width;
    private float height;
    private float x;
    private float y;
    private Bounds stageBounds;
    private Bounds screenBounds;
    private Scene scene;
    private boolean visible;
    private float opacity;

    public Stage() {
    }

    public float getWidth() {
        return width;
    }

    public Stage setWidth(float width) {
        this.width = width;
        return this;
    }

    public Scene getScene() {
        return scene;
    }

    public Stage setScene(Scene scene) {
        this.scene = scene;
        return this;
    }
    ...
}

Finally, the Scene:

package uk.co.xenonique.javafx;

import java.util.*;

public class Scene extends Parent {

    public Scene() {
    }

    public List<Node> getContent() {
        return getChildren();
    }

    public Scene setContent(Node content) {
        this.setContent( content );
        return this;
    }
    ...

}

I have left out the details and the implementation of several standard JavaFX SDK features such as Paint and Effect types. As Sun.Oracle develops their API these will no doubt be answered.

So how on earth do we call this Java API from Scala? What could it look like? Here is a work-in-progress Domain Specific Language example based on my own attempts of modelling these Java API classes. So far it looks like this:

object TestDSL {
  def main(args: Array[String]) {
    val builder = new FXBuilder()
    import builder._

    val s1 = stage("Demo") { s =>
      val s2 = scene { s =>
        group { g =>
          rectangle() { n =>
            n.setX(50)
            .setY(50)
            .setWidth(100)
            .setHeight(200)
            .setFill( new Color( 0xFFCC00 ) )
            .setStrokeWidth(2.0F)
            .setStroke(new Color( 0xCC9900) )
          }
        }
      }
    }
    s1.setVisible(true)
  }
}

Writing a Scala DSL is not easy to tell the truth. Having attended the Scala LiftOff USA on the Friday after JavaOne 2010, I sought some advice, notably from Bill Venners and anyone else expert enough to answer DSL questions.  The best advice I was given, simply, was to take things rather slowly. Build my DSL steadily and deliberately, and the more experience Scala enthusiasts suggested starting with an internal DSL. The DSL above is an internal one. Here are some observations so far:

  • The Scala DSL at this stage is not at all a full replacement for JavaFX Script or Visage project. Scala does not support declarative instantiation of objects in 2.8.0-final.
  • The closures in Groovy have an implicit “it” parameter that are passed to them, in this way Groovy Builders can hide an implicit context. See examples of code that use Groovy’s XML, Ant and Swing builders and you will see this. In Scala we cannot replicate this. Therefore, please  note the explicit closure parameter inside each of the closures.
  • Each closure parameter represents the JavaFX Java object in question. Hence, we can chain the operations together in the form n.setX(10).setY(10).setWidth(100).setHeight(100) etc in the named parameter idiom. (Perhaps a future version of Scala might support optional declaration of attributes on a Scala class).
  • Because of forthcoming binding, therefore Scala case classes cannot be used to implement thin wrappers over the JavaFX 2.0 Java peer classes. Case classes are immutable.
  • Due to the way Scala is calling Java classes it is not possible to support in-fix operations directly. So the following does not cut it: n setX 10 setY 10 setWidth 100 setHeight 100.
  • The advantage of using an FXBuilder and an explicit closure parameter should make binding using the official Java APIs easier rather than harder. If you need to call a specific API routine your closure has the object instance.
  • The FXBuilder DSL is a lightweight wrapper around the JavaFX for Java API.
  • The FXBuilder also knows how to add a Node to Parent. It would have to be extended to handle collections of nodes, namely List[Node].

Of course, Scala is a statically typed language and there is great type safety with it. Getting the code with the mock JavaFX API classes to compile required some enduration as my knowledge of Scala does not quite match my long Java experience just yet. It is the first cut and writing any DSL is a long road and an investment. The equivalent JavaFX Script/ Visage looks this:

def s1:Stage = Stage {
   scene: Scene {
       content: Group {
            content: Rectangle {
                x: 50 y: 50 width: 200 height: 200
                fill: Color.web("#FFCC00")
                strokeWidth: 2.0 stroke: Color.web("#CC9900")
            }
       }
}

All the fun of the fair is the phase that Brits say to there is the road is open, optimistics and fun can be had. Scala and JavaFX does look good at this point. This is Peter Pilgrim. Out.

PS: I am all ears for your opinions by the way. Get in touch please with your views.

Hey all! Thanks for visiting. I provide fringe benefits to interested readers: checkout consultancy, training or mentorship Please make enquiries by email or
call +44 (0)7397 067 658.

Due to the Off-Payroll Working plan for the UK government, I am enforcing stricter measures on contracts. All potential public sector GOV.UK contracts engagements must be approved by QDOS and/or SJD Accounting. Please enquire for further information.