This chapter describes the overall architecture of Metawidget.
The first goal of Metawidget is to inspect existing back-end architectures and generate UIs for existing front-end frameworks. However there are a large variety of back-end architectures and many different types of front-end frameworks. Metawidget's approach to managing this diversity is not to define lots of little 'flags' to tweak lots of variables, but to establish a pipeline with plug-in points along the way for your own custom classes. This five stage pipeline is shown on the right of Figure 2.1. It is co-ordinated by a platform-specific Metawidget class (e.g. SwingMetawidget, StrutsMetawidgetTag etc) as shown on the left.
The five stages of the pipeline are:
an Inspector that inspects specific back-end architectures. This can be a single Inspector or a list of multiple Inspectors (e.g. PropertyTypeInspector, JpaInspector etc) by using CompositeInspector (see Section 1.1.6, “Combining Multiple Inspection Results”). In the latter case, the multiple inspection results are all combined into a single result.
a list of InspectionResultProcessors that can process and modify the inspection result. These can be used to sort properties (e.g. ComesAfterInspectionResultProcessor), exclude properties, and so on. InspectionResultProcessors consider the overall result, holistically, whereas subsequent stages of the pipeline are all 'per widget'.
a WidgetBuilder that builds widgets for specific front-end frameworks. This can be a single WidgetBuilder or a list of multiple WidgetBuilders (e.g. HtmlWidgetBuilder, RichFacesWidgetBuilder etc) by using CompositeWidgetBuilder. In the latter case, the first WidgetBuilder to return a widget is used. In this way WidgetBuilders for third party UI component libraries (that provide specialized components) can be listed first, returning null for property types they do not support. WidgetBuilders for the platform's standard components can be listed last (as a 'fallback' choice).
a list of WidgetProcessors that can process and modify each widget. These can be used to add data binding, event handlers, validation, tooltips and so on.
a Layout that lays out each widget on the screen, possibly organising them into columns (e.g. HtmlTableLayout) or decorating them with other widgets (e.g. TabPanelLayoutDecorator).
|All Inspectors, InspectionResultProcessors, WidgetBuilders, WidgetProcessors and Layouts are required to be immutable. This means you only need a single instance of them for your entire application. If you are using metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”) then ConfigReader takes care of this for you, but if you are instantiating them in Java code you should reuse instances. Keeping everything immutable allows Metawidget to be performant at the same time as being pluggable.|
|The five stage pipeline is the result of many years of developer interviews, case studies and research. For complete details on the theoretical foundations underpinning Metawidget, please see this PhD thesis.|
The following sections discuss each of the five stages of the pipeline in detail.
Metawidget comes with a native component for each popular UI framework. This section discusses characteristics common to all Metawidgets. For in-depth documentation of a Metawidget for a specific UI framework, see Chapter 3, Metawidgets.
Metawidgets are not required to extend any Java base class or implement any Java interface. This is because most UI frameworks require widgets inherit one of their base classes, such as javax.swing.JComponent or javax.faces.UIComponent, and Java does not support multiple inheritance.
In addition, while all Metawidgets support roughly the same functionality, different UI frameworks have different in-built capabilities. For example, JSF has UIComponent.setRenderer for choosing different layouts for the same widget, whereas SwingMetawidget has to roll its own setMetawidgetLayout method. This diversity of capabilities means there cannot be a common 'Metawidget Java interface' either.
However, despite not extending any common base class or interface, all Metawidgets follow roughly the same design, with roughly the same method names:
setToInspect is called to set the Object for inspection. The user typically calls this method, either directly or through some intermediate language (e.g. using a JSP attribute).
internally, buildWidgets is called to begin the process. It first calls Inspector.inspect to return inspection results, then InspectionResultProcessor.processInspectionResult to process (i.e. sort) them.
buildWidgets calls WidgetBuilder.buildWidget to choose a suitable widget for the top-level element of the inspection result (based on its @type and other attributes). If WidgetBuilder.buildWidget returns such a widget, skips to 6.
if WidgetBuilder.buildWidget returns null for the top-level element, calls buildCompoundWidget to iterate over each child of the top-level element.
for each child, calls WidgetBuilder.buildWidget and adds the returned widget to the Metawidget. If WidgetBuilder.buildWidget returns null for a child, creates a nested Metawidget.
the created widgets are passed through a series of WidgetProcessors. These can apply binding mechanisms, validators and so on.
as a final step, the created widgets are passed to a Layout.
For those looking to write their own Metawidget (say, for a currently unsupported platform) there is a BasePipeline class that implements the above steps 2-7 for you, see Section 2.1.4, “Implementing Your Own Metawidget”. All of the supplied Metawidgets are implemented using this class.
As much as possible, Metawidgets defer to the existing Look and Feel technology of their native UI framework. For example, HtmlMetawidget uses HTML/CSS, SwingMetawidget uses Swing Look-and-Feels, and AndroidMetawidget uses Android styles and themes.
The one visual area Metawidget does control is how the widgets it creates are laid out. Typically this is in a tabular 'one column for the label, one column for the widget' format, but this is pluggable.
Metawidgets come with different Layout classes that can arrange the widgets in different ways, and these are set on the Metawidget in a framework-specific way. For example, JSF uses <m:metawidget rendererType=""> whereas SwingMetawidget uses setMetawidgetLayout. As much as possible, the Layout classes defer back to the capabilities of the native framework. For example, Swing's GridBagLayout or Android's TableLayout.
Some Layouts may add localized labels to the widgets whereas other Layouts will leave them unadorned. Different Layouts may support different parameters (e.g. a TableLayout may support numberOfColumns). These are initialized on the Layout at construction time using an xxxLayoutConfig. Finally, some Layouts can be wrapped around other Layout to provide section headings (e.g. TabPanelLayoutDecorator).
Metawidget tries to automate much of the widget creation, but provides many hooks to customize the process:
Stub child widgets can be used to suppress widget creation entirely or to replace automatic widget creation with one or more other widgets with different bindings.
Facet child widgets pass through to the chosen Layout as decorations (such as button bars).
InspectionResultProcessors can be plugged in to the pipeline to include, exclude or sort business object properties.
WidgetBuilders can be plugged in to the pipeline to control widget creation. They can be configured through xxxWidgetBuilderConfig classes.
WidgetProcessors can be plugged in to the pipeline to fine-tune widget properties. They can be configured through xxxWidgetProcessorConfig classes.
Layouts can be plugged in to the pipeline to control layout. They can be configured through xxxLayoutConfig classes.
Metawidget creates widgets native to a particular UI framework. Having to implement your own Metawidget should be far less common than having to implement your own Inspector, InspectorResultProcessor, WidgetBuilder, WidgetProcessor or Layout, but if your chosen UI framework is not supported 'out of the box' you may need to implement your own.
Metawidgets are not required to extend any base class or implement any interface. However, it is recommended developers familiarize themselves with existing Metawidgets (such as UIMetawidget) to make their API similar. Whilst there is no Metawidget base class, all the built-in Metawidgets reuse BasePipeline to ease their implementation. It provides pre-built functionality including: configuring and executing all stages of the pipeline; deciding when to use single versus compound widgets; read-only mode; and tracking maximum inspection depth.