Skip to main content

Thinking Declaratively in JavaFX

Posted by javakiddy on May 26, 2009 at 11:23 AM PDT

JavaOne is coming up, and with it no doubt a slew of enhancements to JavaFX. Many of you reading will have no doubt dipped your toe into the waters of Sun's new platform, but how well do you really understand the power of its Domain-specific language, JavaFX Script?

I've had a pretty good excuse to write lots of JavaFX code of late, and to be exposed to some of the difficulties programmers first have when approaching the language. One of the common stumbling blocks seems to involve the shift in thinking from purely procedural source code (where the function/method is our chief currency) to the declarative source code supported by JavaFX Script (where code may be nested into a tree structure).

So, what I'm going to do in this post is take a problem ripe for declarative exploitation, and show how JavaFX Script might change the way we tackle the solution. For newbies struggling to understand the power of JavaFX, hopefully this will be an eye opener. For the uninitiated, perhaps it might demonstrate how JavaFX Script differs from Java and other languages.

The example I've chosen is the parsing of an XML file — a simple enough task one might think, but a task which highlights the stark contrasts between the structure of the document being processed, and the structure of the source code processing it. (Hopefully that last sentence will make a bit more sense by the time you've finished reading this posting!)

To parse XML in JavaFX we would typically reach for the javafx.data.pull.PullParser class. PullParser walks the document from start to finish, breaking the XML up into a stream of events: for opening tags, for closing tags, and for any loose text inside them. We register a function to receive the events as they come in, and do with them as is our wont. It all seems very straight forward, so where's the problem?

Consider the (slightly contrived) XML below:



    1
    Shop example
   
       
            Joe Smith
           
123 Java Street

       

       
            Fred Bloggs
           
456 Duke Road

       

   

   
       
            Widget Maker
            20 groats
       

       
            Chocolate Fireguard
            99 credits
       

   

Both and contain elements, and both elements are at the same level in the document. JavaFX's PullParser tells us about the element causing the event, but little of where the element is in the document as a whole (aside from its level). This results in long blocks of conditional code, along the lines of...

If the event is a 'text' event, and our immediate parent is , and it was at level 3 in the document, and its parent was ...

A bit of a mouthful, just to ensure we know which bit of text in the XML we're processing. And remember, the only way of knowing the lineage of an event is to keep track of where we are in the document ourselves.

Things get even messier when we want to populate objects as we process the XML. Suppose we created a couple of classes, one to hold customer details, the other to hold product details, and we intend to populate and store objects of these classes as we walk over the XML.

class Customer {
    var name:String;
    var address:String;
}
class Product {
    var name:String;
    var price:String;
}

var title:String;
var customers:Customer[];
var products:Product[];

Because each XML element is delivered as separate, discrete, events we can't use a local Customer/Product object; any objects we create must persist over multiple event function calls, giving us little alternative but to use private instance variables for this temporary data.

For any non-trivial XML document the event handling code quickly grows in complexity and acquires a burgeoning collection of private variables for maintaining state. The code starts to become a tad spaghetti-like, and inflexible — even a simple change to the XML format may require a lot of code hacking. The problem is, of course, we're trying to apply a very linear way of working to a non-linear data structure. If only we could write our event handling code as a tree, so it would mirror the XML.

Expert JavaFX Script programmers will be able to guess what's coming next.

def shopEventTree = Element {
    name: "shop"
    content: [
        Element {
            name: "title"
            onText: function(ev:Event) { title=ev.text; }
        } ,
        Element {
            name: "customers"
            content: [
                Element {
                    name: "customer"
                    var c:Customer;
                    onStart: function(ev:Event) { c=Customer{}; }
                    content: [
                        Element {
                            name: "name"
                            onText: function(ev:Event) { c.name=ev.text; }
                        } ,
                        Element {
                            name: "address"
                            onText: function(ev:Event) { c.address=ev.text; }
                        }
                    ]
                    onEnd: function(ev:Event) { insert c into customers; }
                }

            ]
        } ,
        Element {
            name: "products"
            content: [
                Element {
                    name: "product"
                    var p:Product;
                    onStart: function(ev:Event) { p=Product{}; }
                    content: [
                        Element {
                            name: "name"
                            onText: function(ev:Event) { p.name=ev.text; }
                        } ,
                        Element {
                            name: "price"
                            onText: function(ev:Event) { p.price=ev.text; }
                        }
                    ]
                    onEnd: function(ev:Event) { insert p into products; }
                }
            ]
        }
    ]
}
Element is the first of two JavaFX classes designed to solve our problems. You can see it in action above, being used to create a tree which mimics the XML document we're loading. Each Element relates to a markup node in the document, with name representing the node's tag name. Three event functions (onStart(), onText() and onEnd()) fire on the opening and closing tags, or any loose text inside.

Note how we now handle the creation of those Customer and Product objects (see the highlighted text in the listing). The object is local to the part of the structure it belongs to: onStart() creates it, the corresponding onEnd() stuffs it into the sequence, and in-between the nested Elements populate it.

def xeh:XMLEventHandler = XMLEventHandler {
    eventTree: shopEventTree;
}
def parser = PullParser {
    documentType: PullParser.XML;
    input: (new java.net.URL("{__DIR__}test.xml")).openStream();
    onEvent: xeh.handleEvent;
}
parser.parse();
parser.input.close();

The Element class has a companion, XMLEventHandler, which accepts the Element tree and provides a function for PullParser to call with its events. XMLEventHandler tracks the XML's progress and directs the flat PullParser event to the related Element event function in our event tree. XMLEventHandler can tell the difference between the in and the in , so we no longer need to worry!

The above is only an example, and far from perfect. For large XML documents the Element tree grows to hundreds of lines — hardly ideal. A more professional solution might demand the breaking down of the XML into several sub-trees, which we could model independently and re-use as necessary. Still, Element and XMLEventHandler serve their purpose as a demonstration of the power of thinking declaratively.

The beauty of JavaFX Script lies in its ability to shape code around the data structures we work with. By utilising JavaFX Script's declarative syntax we were able to transform a (potentially troublesome) linear event handler into an event tree who's source code mirrored the document being parsed. The result was more readable, flexible, and easier to maintain. So, bottom line: don't just think about code to manipulate your data, start thinking about code which mimics (models) your data... :)

[Source code] (3k)

Related Topics >>

Comments

Your approach to XML parsing

Hi Simon,

I recently started working on a JavaFX based open source (Apache License) project (Name undecided, but might be on java.net itself in a few weeks - if I and others could complete the initial code for upload).

I really liked the approach you have taken for parsing XML here and I would like to adopt it in the open source project I am working on (of course with your permission and at my own risk) by making slight name changes to your classes for the context I am using them, but will give you credit in the source code itself as the original author for the corresponding JavaFX classes

Since there was no license and terms specified in your attached source code, I thought I'd run this by you and check if it is okay.

-Srikanth Shenoy

@meldahl : "Thanks for your reply. XML itself is recursively defined as a tree of nodes of unlimited depth. There is much evidenced that the mind itself organized concepts as encompassing each other in a hierarchical manner. " That's why I was trying to create a hierarchy from the event handlers that process the document. ;) When your processNode() gets called for an element called [name], how does it know whether it's [name] inside [customer], or [name] inside [product] ...? In my solution the event handling code for [name] never needs to ask whether it's a child of [customer] or [product], because the hierarchy implies the context. Procedural (even recursive) XML processors spend far too much time keeping track of their context (if only for error checking). That was the problem I wanted to fix. Actually it shouldn't be that difficult to adapt my solution to work for infinite hierarchies. Say we had a branch/leaf XML, the event handler for branch->branch (a branch elenent within a branch element) simply invokes a new XML processor using the same node tree. In this way, we can process any number of nested branch elements (and each leaf) ;)

Thanks for your reply. XML itself is recursively defined as a tree of nodes of unlimited depth. There is much evidenced that the mind itself organized concepts as encompassing each other in a hierarchical manner. My "branching" example was provided as a simple example of a nesting paradigm that is ubiquitous in data organization (a hierarchical tree has even been used as a symbol of computer science). My small algorithm works for all XML and limits the schema dependency to the definitions of the individual elements only rather than the fragile structure of the whole schema definition. In any case, I appreciate your example of the power of declarative programming. It was a worthy experiment for me and has influenced my design. Thanks.

@meldahl : "To elaborate on the observation of the previous comment, XML is a hierarchical model and requires a recursion as a general solution. There is no limit to the possible depths of nodes in an XML document and only recursion can guarantee that those depths will be reached. " The idea is to create a specific declarative structure which matches a specific XML schema. Although, in theory, an XML documents can have element types nestable within themselves (like your "Branching" element, creating unlimited depth), in practice most schemas don't permit this (or only do so in a few locations within the document). This is particularly true when dealing with web services -- remote functions which return tree structures of unlimited depth are the exception, not the norm. Although, admittedly, my solution doesn't work 100% of the time, it makes life a lot easier for the 99% of cases it does work for -- meaning you only have to fall back to long-hand procedural/recursive coding for the other 1%. ;)

Actually, in the pseudocode submitted in the previous comment, the hashMap should be keyed on "n.nodeName()" rather than "n.nodeType()".

A method of using the declarative power of JavaFX in a solution that requires recursion (such as XML) would look something like the following pseudocode: ------------------------------------------ function processNode( n: Node) : CompositePatternObject { ......NodeDeclaration d = declarationHashMap.get( n.nodeType() ); ......CompositePatternObject { ............parentObject : d.createUserObject( n ) ............childrenObjects: recursively call processNode() for each childNode ......} } ------------------------------------ The CompositePatternObject is simply a wrapper for the user object created from the current node attached to the children user objects created from child nodes. The primary thing is that for each XML schema, the user would have to load the HashMap with declarative object factories that would basically use "object literal" syntax as found in your solution above.

I greatly admired your declarative effort but it is simply not a solution to the challenge of XML. We need to exploit the powers that a new language gives us but there are limits to these powers and XML reveals the limits of JavaFX's declarative power. To elaborate on the observation of the previous comment, XML is a hierarchical model and requires a recursion as a general solution. There is no limit to the possible depths of nodes in an XML document and only recursion can guarantee that those depths will be reached. Recursion requires, by definition, a procedural solution rather than a declarative one (this procedural solution does not rule out an object-oriented model where objects have procedural behavior). For example, take an XML schema that allows for only two kinds of elements: "Leaf" elements that cannot have children and "Branching" elements that can have children of both the Leaf variety and more Branching elements (Branching elements can recursively contain more Branching elements). Branching elements simply cannot be resolved in a declarative manner. The solution to the Branching elements requires a procedural call from the function that resolves Branching elements to the same function that will resolve the Branching elements at the next lower layer. I have taken your idea however, and have come up with a solution that exploits the declarative power of JavaFX with recursion. I will provide it in detail in the coming weeks on my blog site http://xmlbi.blogspot.com/.

@opinali: I like the idea, but I can't see how one could effectively bind directly against elements of the XML, as a lot of XML has repeating blocks. Given that a lot of XML tends to consist of the same sub-structures repeating over and over inside a container (like the above were [customer] is the sub-structure and [customers] is the container) I wonder if it's possible to use a FOR loop to iterate over the container, and the Element tree to translate each sub-structure into an object? What we need is a sequence which wraps a given part of an XML DOM. Hmm...

I think one of the most fundamental, and often overlooked feature of JavaFX Script is its declarative programming style. It is this declarative style that gives it such an expressive power, and enables developers to so easily use higher level abstractions to represent, and manipulate low level primitive 2D constructs in their applications. As this nice example has shown, libraries/utility packages developers have yet to fully appreciate, and take advantage of the expressive power of JavaFX's declarative style of programming. It takes a little bit of an effort, and clear thinking to not allow ourselves be influenced by such a long heritage of purely procedural style of programming. This declarative style is also a perfect fit with the functional programming aspect of the language and the combination gives such a tremendous expressive power. I believe this declarative style is what really sets apart from other functional programming languages like Scala, and Clojure. JavaFX in a way makes functional programming more approachable, more mainstream, more blue collar so to speak. There are some important things though that are still missing from the language which could potentially make JavaFX the main programming language of the next decade, such as actor based concurrency model, and software transactional memory, and other things. If, and when it gets those it will be unmatchable.

This is very good, but I think JavaFX has potential do do better. I don't like that you have to declare variables and assign values to properties; this is not purely declarative. We could have a more advanced parser library that allows to, somehow, bind object properties to parts of the XML (perhaps with XPath?), so the "parser" would be mostly an object initialization tree, something like this: Customer { name: xmlBind("customer/name") address: xmlBind("customer/address") } This is just a very simple and raw example of what I mean. The most important principle is: having no explicit assignments (so we can parse stuff into public-init properties; public [writeable] properties suck in a language with good potential for the functional style). A decent proposal would show how to use generators to initialize a sequence from a XPath that returns multiple elements; allowing either full XPath expressions or "local" qnames considering scope; perhaps even type inference (with help of a XSD available at compile time) so you don't have to convert data to non-String properties...