Sunday, December 30, 2007

Death of a WidgetBuilder

Things consolidated a bit today.

Until now I've had a Metawidget interface with a MetawidgetImpl class. This acted as a go-between between instantiating platform-specific WidgetBuilders, and running and collating results from multiple Inspectors.

In turn, the platform-specific WidgetBuilders (SwingWidgetBuilder, FacesWidgetBuilder, etc) implemented a WidgetBuilder interface and extended a generic WidgetBuilderImpl class which took care of some of the generic 'plumbing' of iterating over inspection results.

Then we had another class that extended each target framework and made use of the WidgetBuilder. So you have:
  • FacesMetawidgetComponent extends javax.faces.UIComponent (platform-specific base class)
  • FacesWidgetBuilder extends WidgetBuilder (generic base class for iterating inspection results)
  • MetawidgetImpl (collates inspection results)
  • JavaBeanInspector implements Inspector
This worked, but over time I've ended up with such a close relationship between FacesWidgetBuilder and FacesMetawidgetComponent it no longer justifies the separation. So I've collapsed the two. It has the downside of some duplication between, say, FacesWidgetBuilder and SwingWidgetBuilder in how they iterate over inspection results - but I think I can live with that.

In addition, I collapsed the separation between Metawidget and the Inspectors into simply a MetaInspector that both implements Inspector and collates multiple results. This is really neat because it means we can efficiently support apps that only need a single Inspector (they don't use MetaInspector) and formalize a common XSD for inspection results - both from the 'bottom-level' Inspectors and the 'top-level' MetaInspector.

It means there's no longer a separate Metawidget class per se, but I think I can live with that too.

As a final bonus it allows for pluggable implementations of, say, remoting (RemoteInspector) and alternative merging algorithms.

Phew! A lot of refactoring for one day...

Friday, December 21, 2007

Abandon XPaths, All Ye Who Enter Here

I've been trying really hard to hang on to using XPaths as the way to tell the Inspectors what to inspect. It just feels 'right', given that XML is a natural choice for what to return from Inspectors.

  1. Whilst XPaths work quite well for object graphs, which are inherently nested, they are bad for targets like struts-config.xml which are not nested - and nesting them involves recombining lots of different potential combinations
  2. Whilst the approach is cute, it is not true: the XML returned by an Inspector may bear no relation to what that Inspector inspects: in the case of struts-config.xml, the format may be quite different, and in the case of object graphs there is no XML
  3. We actually don't want the expressiveness of XPaths. We want person/current, but we don't want WidgetBuiders being able to ask for, say, starts-with( @type, 'org.example' )
  4. We would have to ship JXPath (no biggie)

Instead, I'm going to try simply using an array of names (eg. 'person', 'current'). This is easier all round to understand, and less prone to abuse.

Sunday, December 16, 2007

Traverse Objects, not Classes

Another big design goal for Metawidget is that it do its work at runtime: I've long had a bee in my bonnett about those who espouse the benefits of static code generation. Yes, static code generation seems to get you started quickly, but in reality it generates volumes of code that is often of poor quality (very verbose, few comments, etc) and still has to be read and understood, documented, handed-over etc.

Runtime frameworks, on the other hand, generate zero code and whilst the frameworks themselves still need to be understood, there is usually lots of documentation how to do so. They also have the benefit of being able to work with the dynamic, runtime nature of a system, which is always richer than the static, compile-time information.

So for example a class Foo might declare a method getBar() which at compile-time is declared to return a Bar, but at runtime turns out (in some instances) to return an ExtendedBar. By examining runtime information, Metawidget can generate better, more accurate interfaces.

Still, this begs the question: if we are traversing objects in favour of classes, what happens if getBar() returns null? Should we stop traversing, or should we fall back to traversing the class heirarchy?

The latter approach is tempting, because it means we can display user interfaces for null objects: it seems a logical way to allow initializing new objects. However, in practice it's awkward because, given we already know the underlying data model is null, we have nowhere to send the data the user interface captures.

So for now I'm going with the first approach: traverse objects, not classes, and stop if we hit null. We'll see how it works out.

Friday, December 14, 2007

XML Is Your Friend

Though less efficient than returning a binary format, XML seems a natural choice for what Inspectors should return:
  • it's technology-neutral, so any underlying platform can produce it, and any technology can be sensibly mapped to it (from object graphs to database schemas)
  • it can be serialized across process boundaries (so the back-end can Inspect and send results to the front-end)
  • it prevents accidentally keeping references to 'live' objects, and accidentally abusing those references

I have some uncertainty about whether to return the XML as a String or a DOM or a SAX Source. I'm going for a DOM for now, but we'll have to see how it shakes out.

Wednesday, December 12, 2007

WidgetBuilders Run The Show

One of the design goals for Metawidget is separating the 'gathering of metadata' from the 'application of metadata to construct user interfaces'. So far, I've settled on two broad abstractions: Inspectors and WidgetBuilders.

The Inspectors will implement some common interface and allow a uniform way to inspect a variety of back-ends (JavaBeans, XML configuration files, annotated POJOs, etc.) to look for metadata. The WidgetBuilders will be tailored to each platform (FacesWidgetBuilder, SwingWidgetBuilder, etc.) and construct a native UI. Metawidget itself will sit in the middle, co-ordinating the interaction between the Inspectors and the WidgetBuilders.

From there, something became apparent pretty quickly.

Originally, I was going to have the Inspectors mine as much as metadata as they could from the back-end, then pass the result to the WidgetBuilder for construction. This certainly has an elegance about it.

However, in practice the developer must be able to override certain portions of the finished interface, either to insert new components or hide existing ones. In the latter case, the developer may choose to hide a portion of the interface (intending, perhaps, to move it to a separate screen) that cuts out a whole branch of the Inspector's metadata.

If the Inspectors have already inspected that metadata, this could be very inefficient. Furthermore, unless the Inspectors are told where to stop, how do they know how far to go? Should they traverse every possible piece of metadata they find? How do they know what their WidgetBuilder will ultimately need? It may depend which UI screen the WidgetBuilder is serving?

It seems the best way to solve this is to flip things on their head: have the WidgetBuilders drive the Inspectors. The user will instruct the WidgetBuilder what aspect they want to render, the WidgetBuilder will see what's involved (minus those parts of the UI the user has opted to override), and will then proceed to ask the Inspectors (via Metawidget) for metadata. Based on what they get back, the WidgetBuilders may later ask for further metadata.

This 'feedback cycle' is perhaps a little less intuitive than the input/process/output approach, but I think works much neater in practice.

Friday, December 7, 2007

Metawidget - What's in a Name?

The wife and I spent a few hours last night brainstorming project names - which is far harder than it at first seems - and finally settled on Metawidget. The idea behind the name is:
  • A 'widget', while being a great word in and of itself, is infact the 'official' name for a Graphical User Interface control/component
  • The 'meta' prefix describes how Metawidget widgets will be constructed of more traditional widgets (text boxes, radio buttons, etc) - Metawidgets are 'widget of widgets'
  • It also implies a relationship to 'metadata', as Metawidget will mine metadata for information on how to construct its interfaces