WidgetProcessors allow arbitrary processing of a widget following
its building by a WidgetBuilder and before its inclusion in the Layout.
This section covers WidgetProcessors in general. For in-depth documentation of individual
WidgetProcessors see Chapter 6, WidgetProcessors.
All WidgetProcessors must implement the WidgetProcessor interface.
This interface defines three methods:
void onStartBuild( M metawidget ); W processWidget( W widget, String elementName, Map<String, String> attributes, M metawidget ); void onEndBuild( M metawidget );
The first method, onStartBuild, is called at the start of the widget building process,
before the WidgetBuilder is called. WidgetProcessors may wish to act
on this event to initialize themselves ready for processing. However it is acceptable to do nothing. A
convenience base class, BaseWidgetProcessor, is provided with a dummy implementation
so that subclasses don't have to override this method.
The last method, onEndBuild, is called at the end of the widget building process,
after all widgets have been built and added to the Layout. WidgetProcessors may wish to act
on this event to clean themselves up following processing. However it is acceptable to do nothing. The
convenience base class, BaseWidgetProcessor, has a dummy implementation
so that subclasses don't have to override this method.
The middle method, processWidget is the most important. It is called for
each widget. WidgetProcessors can modify the given widget according
to the given elementName and various attributes. They can use
the given metawidget to help them if needed (for example, to access a UI context with which
to create validators).
The processWidget method must return the processed widget. This is typically the same as the given
widget. The parent Metawidget then passes this to the next WidgetProcessor
in the list as show in Figure 2.5.
In most cases the WidgetProcessor will simply be modifying the given widget
(adding validators, changing styles and so on). However it can decide to swap the widget out by returning a different
widget. This new widget will then be passed down the list as shown in
Figure 2.6. For an example use of this capability, see
HiddenFieldProcessor.
Alternatively, the WidgetProcessor can decide to exclude the widget entirely by
returning null. Subsequent WidgetProcessors will not be called, as
shown in Figure 2.7, and no
widget will be added to the Layout.
The list of WidgetProcessors is maintained by the parent Metawidget, and is changeable (this
is different to say, CompositeInspector or CompositeWidgetBuilder whose
lists are immutable). This capability is designed to allow easy attaching of event handlers, and scenarios such
as inner classes that have connections to their parent class:
final Object someObject = ...; metawidget.addWidgetProcessor( new BaseWidgetProcessor() { JComponent processWidget( JComponent widget, String elementName, Map<String, String> attributes, SwingMetawidget metawidget ) { ...decide whether to attach event handler... widget.add( new AbstractAction() { public void actionPerformed( ActionEvent event ) { someObject.doSomething(); } } } }
Most Metawidgets have default WidgetProcessors. Overriding the default means the default is no
longer instantiated. Usually this is not what you want, so you should consider instantiating
the default along with your new WidgetProcessor. You can see the default by looking in the
Metawidget JAR for the file metawidget-xxx-default.xml (where xxx
is your target platform, such as swing or struts).
For reference, the defaults are:
| Platform | Default |
|---|---|
| Android | (none) |
| GWT | (none) |
| JSF | <array> <requiredAttributeProcessor /> <immediateAttributeProcessor /> <standardBindingProcessor /> <readableIdProcessor /> <labelProcessor /> <standardValidatorProcessor /> <standardConverterProcessor /> <cssStyleProcessor /> </array> |
| JSP | (none) |
| Spring | (none) |
| Struts | (none) |
| Swing | <array> <reflectionBindingProcessor /> </array> |
All WidgetProcessors are required to be threadsafe and immutable. This means you only
need a single instance of a WidgetProcessor for your entire application. If you are using
metawidget.xml then ConfigReader takes care
of this for you, but if you are instantiating WidgetProcessors in Java code you should
reuse instances.
Although individual WidgetProcessors are immutable, the List they
are contained in can be changed. Methods such as addWidgetProcessor allows clients to dynamically
add WidgetProcessors at runtime. This is useful for adding event handlers (see
Section 2.4.1, “Interface”).
Note that immutable only means WidgetProcessors cannot be changed once instantiated - it does not
mean they cannot be configured. Many WidgetProcessors have corresponding xxxConfig
classes that allow them to be configured prior to instantation in a type-safe way. For example, a
BeansBindingProcessor can be configured in code:
metawidget.addWidgetProcessor( new BeansBindingProcessor( new BeansBindingProcessorConfig() .setUpdateStrategy( UpdateStrategy.READ_WRITE )));
Or in metawidget.xml:
<beansBindingProcessor xmlns="java:org.metawidget.swing.widgetprocessor.binding.beansbinding" config="BeansBindingProcessorConfig"> <updateStrategy> <string>READ_WRITE</string> </updateStrategy> </beansBindingProcessor>
Here is an example of a custom WidgetProcessor to add tooltips to
add JComponents. It extends the code from the tutorial
(see Section 1.1, “Part 1 - The First Metawidget Application”).
package com.myapp; import static org.metawidget.inspector.InspectionResultConstants.*; import java.util.*; import javax.swing.*; import org.metawidget.swing.*; import org.metawidget.widgetprocessor.impl.*; public class Main { public static void main( String[] args ) { Person person = new Person(); SwingMetawidget metawidget = new SwingMetawidget(); metawidget.addWidgetProcessor( new TooltipProcessor() ); metawidget.setToInspect( person ); JFrame frame = new JFrame( "Metawidget Tutorial" ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); frame.getContentPane().add( metawidget ); frame.setSize( 400, 210 ); frame.setVisible( true ); } static class TooltipProcessor extends BaseWidgetProcessor<JComponent, SwingMetawidget> { public JComponent processWidget( JComponent widget, String elementName, Map<String, String> attributes, SwingMetawidget metawidget ) { widget.setToolTipText( attributes.get( NAME ) ); return widget; } } }
Like Inspectors, WidgetBuilders and
Layouts, WidgetProcessors are required to be threadsafe
and immutable. However you can still make them configurable by using
xxxWidgetProcessorConfig classes. For example:
public class TooltipProcessorConfig { private String mPrefix; public TooltipProcessorConfig setPrefix( String prefix ) { mPrefix = prefix; return this; } public String getPrefix() { return mPrefix; } // ...must override equals and hashCode too... }
These xxxWidgetProcessorConfig classes are then passed to the WidgetProcessor
at construction time, and stored internally:
public class TooltipProcessor { private String mPrefix; public TooltipProcessor( TooltipProcessorConfig config ) { mPrefix = config.getPrefix(); } }
This mechanism can then be controlled either programmatically:
metawidget.addWidgetProcessor( new TooltipProcessor( new TooltipProcessorConfig().setPrefix("Tip:")));
Or through metawidget.xml:
<tooltipProcessor xmlns="java:com.foo" config="TooltipProcessorConfig"> <prefix> <string>Tip:</string> </prefix> </tooltipProcessor>
![]() | Config classes must override equals and hashCode |
|---|---|
If you want your configurable WidgetProcessor to be cacheable and reusable by
ConfigReader and metawidget.xml, the xxxWidgetProcessorConfig
class must override equals and hashCode.
|
![]() | Generate an XML Schema |
|---|---|
If you intend your WidgetProcessor to be configurable via metawidget.xml, consider
defining an XML Schema for it. This is optional, but allows users to validate their use of your WidgetProcessor in their
metawidget.xml at development time. There is an Ant task,
org.metawidget.config.XmlSchemaGeneratorTask, provided in the source distribution that can help with this
by auto-generating the schema. All the existing Metawidget schemas are generated using this Ant task.
|