Building Rich Swing Applications with Groovy - Part II - No Fluff Just Stuff

Building Rich Swing Applications with Groovy - Part II

Posted by: Andres Almiray on November 30, 2009

The following article appeared on Groovymag's second issue (December 2008). It is the second of a four-part series on Groovy and Swing applications. Be sure to check out Groovymag for more information on Groovy, Grails, Griffon and other GR8 technologies (Gant, Gradle, Gmaven, Spock, etc... :-D)

Building Rich Swing Applications with Groovy - Part II

By Andres Almiray

 

Groovy's Swingbuilder greatly simplifies creating Swing based user interfaces, but as Java developers already know the Swing component suite provided by the JDK does not cover all needs, additional Swing components are required depending on an application's particular requirements. This second article provides a guide of the features provided by SwingBuilder that will let you use other Swing component suites, as well as register your own.

 

On the first article of this series we discussed a bit of Swing's history and some of its problems, which are directly responsible for the emergence of frameworks and technologies aimed on simplifying its usage. Groovy's SwingBuilder is one of such technologies, both the builder and the language provide a DSL (Domain Specific Language) for creating Swing applications in a declarative and intuitive manner.

 

Since its release almost 10 years ago Swing it was clear that the core component suite could not accommodate the needs of future applications in terms of the number of visual components those applications require; fortunately the Swing team decided to add extensions hooks here and there, allowing component suites to be built by third parties, and those suites were built indeed. By doing a quick search on the Internet with keywords as Java Swing component suite you'll find plenty of commercial and/or open source suites, you'll probably be wondering how can those components be used with SwingBuilder, well the Groovy developers also made SwingBuilder extensible enough to accommodate future additions and components, there are plenty of options depending on your needs, this article will serve as a guide to those options.

1. Once-shot Placeholders

Let's pretend for a moment that there is a custom implementation of a JLabel that you would like to use in your next Swing application. This custom component draws a line border always, allowing you to change the color and thickness (yes, I know borders are a fundamental feature of Swing components but bear with me for a bit, I'll make your worth while), that implementation could look like the following one

 

Listing 1.1


 

SwingBuilder nodes can behave like placeholders for any custom component that matches their type, for example a button node can be used to either create new JButton or insert a custom JButton implementation, so in our case label matches to JLabel or any JLabel subclass. Because we know for a fact that CustomLabel is a subclass of JLabel we can use this approach, resulting in the following simple application

 

Listing 1.2




Figure 1. CustomLabel with a thick red border.

 

Beware that you cannot embed a button using a label node nor vice versa, the target component type must match the node's type. But what if the custom component does not extend a well know Swing class? say it extends JComponent directly (as it is the case with many custom components out there), you will be unable to use this approach. Before you fall into despair thinking that Swing development went back to the hard days let me tell you there is a solution for this problem, actually two to be precise. SwingBuilder exposes two special nodes that will let you embed any Swing component, their names are container and widget. Both behave like the previous approach, i.e, will embed a component, the difference strives in that both do not care about a particular type and that container allows nesting of children components while widget does not. That being said the following code with an hypothetical MyCustomComponent results in

 


 

Now you know how custom components can be embedded, but wouldn't it be sweeter if they could be embedded in the same fashion as the default components are? there surely must be a way to make it so, which will be covered in the next section.

2. Adding Nodes

The first part of this series briefly showed how embedding a custom component into SwingBuilder's life cycle can be achieved, by register a node factory. If you're wondering how SwingBuilder manages to convert a node name into a Swing component it is all due to hard working factories. This builder in particular pioneered the factory mechanism, which prompted the Groovy Swing team to refactor it into a more generic builder: FactoryBuilderSupport, later retrofitting SwingBuilder as a subclass of it. FactoryBuilderSupport offers many extension hooks for builders that behave similarly as SwingBuilder,  which we will further explore in the next sections, the first would be of course factories.

The "I'm in a hurry" Way

As you may know already, Swing components tend to follow the JavaBeans conventions: no-args constructor, property accessors; adding observable properties when it makes sense. For this type of components SwingBuilder offers a quick way for embedding them, as shown in the next listing

 

Listing 2.1


 

Notice any difference with listing 1.3? that's right, the node name is now customLabel, an instance of CustomLabel is no longer a prerequisite, lastly you can use the new node anywhere as long as you use the same SwingBuilder instance where the node was registered. This is as simple as it gets as long as the custom Swing component follows the aforementioned rules; this option will work for many of the Swing components that are out there in the wild, SwingBuilder will even manage parent/child relationships to the best of its knowledge; for those components that require a little extra configuration there is an alternative too.

The "Let me micromanage" Way

While many Swing components follow the same rules not every one of them does, take for example the Kiwi Toolkit, one of the earliest open source component suites available. Some of its components provide simple extensions to core Swing components, for instance a KButton is an specialization of JButton that allows customized image backgrounds if place on a container that allows them too (like KPanel, which is you probably guessed it, a customization of JPanel). Problem is that you cannot create a KButton with a no-args constructor so registering a default bean factory will not do. But wait a second, didn't we just say that the power of SwingBuilder comes from its factories? and that it also provides a handy bean factory? if you think it also supports adding other types of factories you'll be on the correct path.

 

Factories come from FactoryBuilderSupport, both classes live in the groovy.util package (which means you don't need to import them, it is done automatically) and provide a basic contract for creating nodes, setting their properties, handling parent/child relationships, allow or veto child content and more. Here is an outline of the current contract sans javadoc

 

Listing 2.2


 

Often times you'll need not be concerned with all of them as the most required custom behavior is creating nodes and handling parent/child relationships. Back to our KButton example we will be needing one of the following to create an instance of it: text, action or icon. Our first attempt to support this component will require a custom implementation of a Factory, fortunately the Groovy Swing team deemed useful to provide a base implementation for you, you just need to fill in the blanks

 

Listing 2.3


 

It is quite simple considering there is an extra constructor that takes two parameters that we're not wiring. Let's register the factory with SwingBuilder and test it out shall we?

 

Listing 2.4


 

That was pretty quick and painless, wouldn't you agree? SwingBuilder includes a few custom factories that handle special cases as frames&windows, layouts and binding (yes we do have binding support! more on that later). You can find those factories under the groovy.swing.factory package, they are a good source to get you started on your own factories.

3. Tapping into the Build Life Cycle

As you now know factories are responsible for handling nodes through its build life cycle, but this doesn't mean that you are required to register a custom factory to tap into that life cycle, again SwingBuilder exposes clever extension hooks to tweak the build process. Actually that statement is half true, it is really FactoryBuilderSupport the one providing those hooks, which means that any builder based on it will have the features that will be described next. Curious about those other builders? take a look at ObjectGraphBuilder and DomainBuilder (from Grails), just to name a few.

 

Perhaps you have noticed on previous examples that some nodes have an id attribute, clearly that is not an standard Swing property and still the code works, as a matter of fact the value of that id becomes a variable reference to the component built with that node, on top of it this feature works not only for any existing node but also for any you may register, how can this be? FactoryBuilderSupport allows registration of custom strategies that will take effect at key points during the build process, it calls these strategies delegates, there are five of them, listed in the order the builder calls them:

PreInstantiate Delegates

Called after the node's factory has been resolved but before the node is actually created. It's responsibility is to verify the parameters used to create the node and possibly update them. It's signature is

 

def preInstantiateDelegate = { builder, attributes, value -> ... }

 

There are no default delegates of this type registered by SwingBuilder.

PostInstantiate Delegates

Called after the node's has been instantiated but before any properties are set on it. It's signature is equal to the previous delegate but takes the built node as parameter instead of the node's value. It's signature is

 

def postInstantiateDelegate = { builder, attributes, node -> ... }

 

There are no default delegates of this type registered by SwingBuilder.

Attribute Delegates

Responsible for inspecting the node's attributes, possibly intercepting synthetic ones. This is actually how SwingBuilder handles the id property. Another useful feature of FactoryBuilderSupport is that each factory comes with its own private context (with the parent context chained in if it exists), which means you can store private data pertaining a particular node while it is being built, once the node has been finished the context disappears so don't count on that private data to exists forever! Its signature is

 

def attributeDelegate = { builder, node, attributes -> ... }

 

Note the slight reordering of parameters when compared to the previous delegate. SwingBuilder registers an attribute delegate that stores a reference to the built node using the id value as the reference name, the id value is temporarily stored on the current context to later be discarded.

PostCompletion Delegates

Called at the end of the build cycle for a particular node, this is a good time to perform cleanup locally or tweak the node according to any nested content it may have registered. Its signature is

 

def postCompletionDelegate = { builder, parent, node -> ... }

 

There are no default delegates of this type registered by SwingBuilder.

Disposal Delegates

Called after the whole building process has finished, these delegates usually perform cleanup tasks. It's signature is very simple as they don't take any parameters at all. SwingBuilder uses disposal delegates to cleanup binding artifacts, have a look at BindFactory to learn more about their usage. On a related note, ObjectGraphBuilder also takes advantage of disposal delegates to resolve lazy references, now you have two examples to learn from.

 

FactoryBuilderSupport exposes a couple more of extension hooks, like resolving factories, which while not relevant to Swingbuilder may come in handy for a particular builder you may be interested in writing.

4. Component Suites

Now that you know more about SwingBuilder's extension points and how nodes can be registered you may be thinking on creating your own builder to support a custom component suite, if you do I hope you let us (the Groovy community) know about it! In the meantime here are a couple of official extensions based on popular component suites

SwingXBuilder

SwingX is a component suite coming from the Swinglabs umbrella project. Its original intention is to serve as an incubator of ideas and components that could be added to the JDK at a later stage. As a matter of fact some of those features already found their way into JDK6, like SwingWorker and JTable highlighters. Inspecting the javadocs you will notice that many SwingX components have a correlation to their Swing counterparts, like JXButton for JButton, JXTable for JTable an so on;  SwingXbuilder follows the convention established by SwingBuilder which means some node names clash and some others don't. In order to reduce ambiguity and be clear you can set a synthetic property classicSwing to true to get the old component (Swing) or leave the node alone and obtain the new one (SwingX), here is an example of the builder in action

 

Listing 4.1


 


Figure 2. Simple application with some SwingX components.

JideBuilder

JideSoft is one of those companies that offer a hide grade, commercial Swing component suite. A couple of years ago they decided to release a considerable percentage of their suite as an open source project, which is known as Jide Common Layer. JideBuilder gives you access to those components in the same manner as the previous builders, there is really not much to it once you have mastered your way around SwingBuilder, just add a node, set its properties and continue hacking. Here is an example of the builder in action

 

Listing 4.2




Figure 3. Simple application with some Jide CL components.

GraphicsBuilder

While not particularly a provider of Swing components, GraphicsBuilder (see update1) ties in with SwingBuilder by following the same conventions but providing graphics nodes than rely on Java2D to draw shapes, images, virtually anything you can draw with Java2D, just with a DSL style approach. Because of this a thorough discussion of GraphicsBuilder warrants a series of its own, l leave you with a teaser of its features

 

Listing 4.3


 


Figure 4. A Flake-like image made out of a Rectangle.

 

The GraphicsBuilder distribution includes an exploratory tool called graphicsPad (see update2) which you can use to play around with graphics nodes, create new drawings, even export them as standalone scripts. A new version is in the works that will also allow you to import/export SVG, export to SWF and popular image formats as GIF and PNG.

Conclusion

We have covered a lot of ground in this article. First we learned to embed any component on the fly so to speak. Then how to make those additions more permanent, by registering either a bean factory or a custom factory. We then dove deep into factories and the build life cycle, where extension hooks are provided for you to tweak and tune the build process as required by your application. Finally we discussed a few of SwingBuilder's cousin builders offered as official extensions.

I know I left one topic out that was promised to be covered later: binding. It will be the main driver for the next article in this series and the last bit of the puzzle that will lead us to the project where all the Swing roads converge: Griffon.



Update #1: GraphicsBuilder is no longer maintained as it has been superceeded by GfxBuilder. Both builders share a lot of features. It should be a simple task to convert a script from one builder into a script the other can understand. However GfxBuilder 0.2.1 does not support filters yet, at the time of writing this update.

Update #2: GraphicsPad is no longer available. It's functionality has been merged with SwingPad.


Keep on Groovying!
Andres Almiray

About Andres Almiray

Andres is a Java/Groovy developer and a Java Champion with more than 20 years of experience in software design and development. He has been involved in web and desktop application development since the early days of Java. Andres is a true believer in open source and has participated on popular projects like Groovy, Griffon, and DbUnit, as well as starting his own projects (Json-lib, EZMorph, GraphicsBuilder, JideBuilder). Founding member of the Griffon framework and Hackergarten community event. https://ch.linkedin.com/in/aalmiray

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »