Validation overlays using multiplex look and feel
Perhaps one of the bigger disadvantages of implementing the validation overlays by extending a specific look and feel is that this technique effectively locks your application into that LAF. While this can be addressed with complicated bytecode injection techniques that might fail under different LAFs, isn’t there another way to add the validation overlay as a simple “add on” on top of any LAF? In this entry we will look at a solution available in the core Swing package – a multiplexing look and feel.
Available from as far back as JDK 1.3, the MultiLookAndFeel class sounds like a perfect solution to our problem. Reading the documentation of this class promises a nice and clean solution:
A multiplexing look and feel [addresses] both these problems simultaneously because it allows multiple look and feels to be combined. The first problem (having to use what amounts to a second copy of the same code) is solved because the developer can create a specialized look and feel that can then be combined with other look and feels. The second problem (having to force the use of a particular look and feel) is solved because a specialized look and feel can be used with whatever default look and feel the application may have locked in place.
With all this promise there are surprisingly few examples of using multiplexing LAF (even given that it has existed for quite some time now). First, you can read this thread on java.net dating back to 2005 (especially the remarks from then-Swing architect Scott Violet). Second, note how this bug is still open after two years. In this entry i’m going to show just a few deficiencies of the multiplex LAF implementation and why you should stay clear of it in most cases.
The classes in this entry are located in the org.pushingpixels.validation.multilaf
package and implement the validation overlays using a multiplex LAF (on top of the Substance LAF). The first implementation takes off by learning all the mistakes from the previous entry – using custom borders and special handling of inner text fields for proper overlays. The ValidationTextFieldUI
and ValidationComboBoxUIFirstTry
do just that. Then, we set Substance as the “main” LAF and add our “validation” LAF (that has these two UI delegates) as an auxiliary LAF (you can have more than one). Here is how our application looks like:
As you can see, the combobox buttons are clearly not coming from the main LAF. What is going on? Our combobox UI delegate extends the BasicComboBoxUI
(so as to allow us to add the validation overlay functionality to any LAF). The arrow button is part of the internal implementation in the UI delegate and is created by the createArrowButton()
, which is called from the installComponents()
. So far so good, since this is how a combobox UI delegate that extends the basic implementation works. The problem with the MultiComboBoxUI
is that it invokes the “lifecycle” methods of the installed UI delegates starting from the main LAF. And so, the MultiComboBoxUI.installUI
first calls SubstanceComboBoxUI.installUI
(which calls SubstanceComboBoxUI.installComponents
which calls SubstanceComboBoxUI.createArrowButton
), and then calls the ValidationComboBoxUIFirstTry.installUI
(which goes through the same sequence of steps, eventually calling the BasicComboBoxUI.createArrowButton
). And so, the arrow button installed by the main LAF is effectively uninstalled by our extension without it explicitly doing so. Note that this would happen under any other LAF; it’s not Substance-specific.
With this very technical detail exposed, our second try in ValidationComboBoxUISecondTry
is to override the createArrowButton()
and return null. Not only this adds an unnecessary implementation complexity to our class (which has absolutely nothing to do with validation overlays, by the way), it also results in a nice NullPointerException
when BasicComboBoxUI.installComponents()
tries to add the null result to the combobox. Obviously, the core Swing implementation of multiplex LAF was never tested under such a simple scenario as adding custom painting to comboboxes.
Our last try is in ValidationComboBoxUI
which has to use very “brittle” techniques to preserve the original button appearance. It overrides the createArrowButton()
, iterates over all UI delegates picking the one that doesn’t come from our auxiliary LAF, finds its createArrowButton()
using reflection (since this method is protected), marks it as accessible and calls it using reflection. Obviously, this is not the best thing to do – the reflection might fail under a stricter security manager and the code is ugly and difficult to refactor.
Here is how our application looks now:
Although the combo buttons look consistent with the application, note how they are four pixels wider than they without our auxiliary validation LAF (18*15 instead of 14*15). What does this mean – yet more exploration of the combobox UI implementation to find which method is responsible for computing the button bounds and, perhaps, yet more unnecessary and unjustified overriding of methods that are not related to our functionality (with possible use of reflection).
So, as you can see, the multiplex LAF is one of the worst techniques that you can choose for the validation overlays and pretty much for any other paint-related functionality. It’s a real pity that after seven years this functionality remains in such unfinished state.