One of the cases I’ve had to deal with in gwt-pectin is creating Reduce style functions that operate on a collection of values. The basic idea is to define interface of the form.

public interface Reduce<R,T> {
R reduce(List<T> source);
}

I use this on my ReducingValueModel<R,T> to automatically compute fields such as a sum’s of numbers, or a string representation of a list and so forth. So on my ReducingValueModel I have a method to configure the reducing function to use as follows.

This works fine when define a new function for each different combination of R and T. The trouble appears when you want to create a generic Reduce function that you can use on any ReductingValueModel. Something like a generic “list to string” style function for example:

But this won’t compile because ListToStringFunction is a Reduce<String, Object> and not Reduce<String, Integer>. So we bung in the standard <? super T> clause on the ReducingValueModel so it can accept a function that works on any super type.

And while it looks like this should work, it doesn’t. The problem is that were I’m using the Reduce function it’s now defined as a Reduce<R, ? super T> (making it a Reduce<T,Object> for all intents and purposes) so it can’t accept any old List<T> as an argument.

Now, all of this may seem a bit harsh, but let’s analyze the relationship a bit closer. For me to do work for you, there’s got to be some kind of social bond; some kind of give-and-take; a bi-directional relationship wherein both parties have given and received. At the point when you’ve come at me with your comment, our relationship consists of one point of interaction: I’ve put work into a project and released it, and you’ve taken the project. See the lopsidedness? You’re not doing me a favor by taking the project: I have no social bond with you because you decide to take my free toilet. If you want me to do some work, a kind of quid pro quo is required up-front: some sign that you’re in the relationship, and the relationship will be give-and-take. I don’t keep careful books on how much I’ve given or taken from people, but in your case, I don’t have to: I’ve given something, you’ve given nothing, and now you’re demanding more. That’s not the kind of relationship I care to be a part of.

And a nice quote from the comments:

I’m more than willing to share because I don’t do open source for fame or ego or whatever: I’ve long since known that I don’t have the skills or the cult-follower-inspiring charisma or the Satanic pact or whatever it is that moves people from “open source developer” to “software developer micro-celebrity”. I do open source because it solves a problem for me, and I think it might help solve a problem for someone else. Or because it’s interesting, and I think someone else might find the stuff interesting, too.

Lately I’ve been pondering and playing with a presentation model style a binding API for GWT. It’s been fun, and I’ve got an initial prototype going. It uses a guice style builder API that makes wiring things up much nicer. You can read the overview/ideas document at:

One of the things to Java/Swing developers face when trying to migrate a application to the Mac is handling the differences in how the Mac displays menus. This can particularly be the case if you’re a microISV and can’t justify the time and effort so support a platform you may rarely use. Still, the number of Mac users are growing and it would be nice to include them if you can.

So if you’re going to deploy to the Mac there are four main things that need to be addressed.

Mac menu items don’t contain icons

Mac menu items don’t have mnemonics.

The default menu accelerator is the Command key (KeyEvent.VK_META) and not Control.

Mac menus often have a different structure. For example, “Quit” is never in the “File” menu, it’s on the Mac provided application menu along with your “Preferences” (think “Tools/Options” on Windows).

These differences can be a huge pain to implement and you definitely want to avoid putting a whole stack of if (Env.isMacOS()) statements in your code. Of the four points above, GUI Commands handles the first three automatically, the fourth is handled using Mac specific configuration files that provide just the Mac specific differences.

Fixing Icons, Mnemonics and the Default Accelerator

GUI Commands automatically provides the correct rendering on the Mac without any developer intervention. The following images show how the “Edit” menu from Mr Schedule appears on both windows and the Mac with the exact same configuration.

GUI commands stores the details of any particular menu item in standard resource bundles and uses a relatively simple key syntax to define each value. The following is a shows the configuration for the “Cut” menu item. They basic syntax is <command-id>@<dotted-property-name>=.... The “face” prefix is used for all visual properties such as the text or icon etc.

editor-cut@face.text=Cu_t@default X
editor-cut@face.icon=classpath:images/cut.gif
editor-cut@face.description=Cuts the current selection to the clipboard (${accelerator})

GUI commands then automatically removes the mnemonic and icon and uses the correct accelerator “⌘X” when running in a Mac environment. The use of ${accelerator} in the description is also important since GUI Commands will inject the correct value for the platform, thus you’ll get “Ctrl-X” on Windows, and “⌘X” on the Mac.

Changing Menus and Other Things

So far, we haven’t had to provide any Mac specific configuration. But to remove the “Quit” item from the file menu we’ll have take a different approach. In this case we need to provide alternate configuration when running on Macs. Importantly, by using GUI Commands you only need to supply the data that changes, i.e. you won’t have any replicated configuration (the DRY principle).

In this example we’ll create a file menu that works correctly for both Windows and the Mac. First off we’ll create the default configuration file, in it we’ll define the “File” menu and an “Open” and “Quit” command. Menus are defined using the prefix group! and we’ll create our settings in a file called Commands.properties.

Now we create MacCommandVariants.properties and put anything that is different for the Mac platform. In our case the file menu has different members and the quit command has different text and a different accelerator.

# The Mac file menu (we only need to include the properties that change).
group!file-menu@members=open-command
# The quit-command has different text and accelerator on the Mac.
quit-command@face.text=Quit@default Q

Now in our code we can do the following.

// load all the commands as normal.
GuiCommands.load("Commands");
if (Env.isMacOS())
{
// Load the Mac specific differences. It's important to
// do this after loading the main file so it can override
// existing settings.
GuiCommands.load("MacCommandVariants");
}

Using Locale Variants

Because GUI Commands configuration files are standard ResouceBundles, you can also use Locale Variants to acheive this same behaviour. In this case you’d configure you locale variant to something like “MAC” and rename your Mac settings to something like Commands_en_AU_MAC.properties. Because of the way the Locale class works, this can be an issue of you want to use the default bundle or don’t want to specify country codes. So unless you’re fully localising your app, the previous method is simpler and easier to understand.

Putting Things in the Application Menu

While GUI Commands doesn’t support this directly, the following shows how to add our Quit command to the applicaiton menu using MRJAdapter.

The slogan of write once run anywhere definitely has it’s limits. In slowing migrating Mr Schedule toward the Apple Human Interface Guidelines the JOptionPane is one of those limits. There are a couple of Mac specific requirements that it just doesn’t support. Amongst other things they include:

The Mac supports document model dialogs that are displayed as a sheet.

The Mac supports the notion of a “destructive option”, things like “Discard Changes”. These options are displayed physically distant from non other options like “Save” or “Cancel”. This is a perfect application of the proximity principle in visual design.

Mac dialogs don’t have a title.

Mac dialogs should display only the application’s icon and not the regular question or exclamation icons.

The following example shows a document model dialog with a destructive option.

Mac Dialog Example

Now the excellent Quaqua library already does much of the work for us including providing a sheet style dialog implementation, but there are a few things that are still missing for easy use in a production environment.

Quaqua does’t provide a unified API for all platforms, it’s still up to the developer to write Mac specific code

The document model sheet implementation isn’t a blocking call. This adds to the level of Mac specific code you need to write as well as adding an additional threading concern.

Now to overcome this we need some kind of framework that delivers following:

An encapsulation of all the data required to show a message to the user including the default and destructive options.

A standard mechanism to display the message to the user with well defined semantics for the developer (i.e. does this call block or not?)

A platform specific implementation the does the “right thing” for the platform.

A factory that automatically gives us the correct implementation based on the current platform

Creating Your Message
Most of this isn’t too hard, and pays off enormously once you use it in a second application. In my code I’ve created a Message object that holds the message title, the main and information messages along with the options available (including if they’re the default or destructive option).

Message myMessage = new Message("This is the title");
myMessage.setMainMessage("Do you want to save the changes to
this message before closing?");
myMessage.setInfoMessage("If you don't save, your changes will
be lost");
// now add the options..
myMessage.addDefaultOption(Option.SAVE);
myMessage.addOption(Option.CANEL);
myMessage.addDestructiveOption(Option.DISCARD_CHANGES);

This creates everything we need to know about the message. I’ve also created Option object instead of the JOptionPane integer approach since objects are nice and play better when using resource bundles.

Displaying the Message
Now we need a mechanism for displaying the message. To do this I’ve created a MessagePane class that acts as both an abstract base class and provides static factory methods for accessing the correct implementation for the platform. You could of course separate these roles, but I chose not to. The concrete instances of MessagePane are created by a service provider interface called MessagePaneProvider. In my world this is configured using Google Guice, but you could just as easily do something like MessagePane.setMessagePaneProvider(...) if you’re not doing the injection thing.

Once our MessagePaneProvider has been configured we can now simply go:

// get a document message pane instance
MessagePane pane = MessagePane.getDocumentInstance(myFrame);
// now call to show to display it. This call will block on
// all platforms
Result result = pane.show(myMessage);
// now check the result
if (result.userChose(Option.SAVE))
{
// save our document...
}
else if (result.userChose(Option.CANCEL))
{
// cancel...
}

For the Mac this will use a MessagePane instance that displays a JSheet and uses some Foxtrot magic to make it a blocking call.

The above example also highlights the shows of a Result object instead of JOptionPanes integer approach. I find result.userChose(Option.SAVE) much more readable for a start, but more importantly result.userChose(…) throws an exception if passed an option that was never included in the message.

JSheet and Foxtrot Magic
The only really tricky aspect of the design was getting the JSheet call to block. The approach I’ve used uses Foxtrot to block the “show” method until until the JSheet callback has been invoked.

public Result
show(Message message)
{
// we use a JOptionPane to do the work. This method creates
// and configures the JOptionPane appropriately for the platform.
JOptionPane pane = createOptionPane(message);
// Quaqua's JSheet is non blocking so we create a blocking
// queque that foxtrot can block on until the selected option
// is placed in queue by the JSheet callback.
final ArrayBlockingQueue

Summary
So just a little bit of thought and effort you can create an API that is consistent and simple for the developer and pushes as many of the platform specific details as possible into the framework implementation.

I always find it healthy to come back and use code you wrote a while ago. There’s nothing like a bit of space to help see things in a new light. Often too you can be so focused on solving one problem that you don’t get a chance to step back and see the bigger picture.

And so it was when using my binding framework the other day. Don’t get me wrong, I really like it. But the first pass gave me flexibililty I needed, but not the simplicity I needed for day to day regular use. Who really wants to create presentation models using the following?

// somewhere at startup
Pectin.registerMixin(ComponentMixin.instance());
// then when you need a presentation model
PresentationModel<ComponentValueModel> pm =
new PresentationModel<ComponentValueModel>();
// and use the "firstName" value model..
pm.get("firstName").setEnabled(false);

What’s a ComponentValueModel anyway and why do I need one? As it turns out I almost always want one (since it gives my value models the common enabled, visible, and editable properties) so why do I have to specify it every time? Then there’s validation, you almost always need that too.

So, short story long, I’ve layered the API. The original classes have been moved to a lower level (i.e. GenericPresentationModel) and I’ve created new implementations that extend them and that come pre-configured with the default interfaces. I’ve also installed the associated Mixins automatically so for everyday use it’s now just:

// This is much better
PresentationModel pm = new PresentationModel();
pm.get("firstName").setEnabled(false);