Metawidget User Guide and Reference Documentation

Version 4.2

Richard Kennard


Table of Contents

Preface
Supported Technologies
1. Introduction to Metawidget
1.1. Part 1 (Java version) - The First Metawidget Application
1.1.1. The Object
1.1.2. The Interface
1.1.3. The Output
1.1.4. Ordering The Properties
1.1.5. Inspectors
1.1.6. Combining Multiple Inspection Results
1.1.7. Controlling The Layout
1.1.8. Controlling Widget Creation
1.1.9. Configuring Metawidget Externally
1.2. Part 1 (JavaScript version) - The First Metawidget Application
1.2.1. The Object
1.2.2. The Interface
1.2.3. The Output
1.2.4. Inspectors
1.2.5. Combining Multiple Inspection Results
1.2.6. Controlling The Layout
1.2.7. Controlling Widget Creation
1.3. Part 2 - The Address Book Application
1.3.1. Desktop Address Book
Read-Only Mode
Binding
Localization
1.3.2. Web Address Book
Mixing Metawidgets
Expression Based Lookups
Alternate Widget Libraries (JSF 1.x)
Alternate Widget Libraries (JSF 2.x)
1.3.3. Mobile Address Book
1.3.4. Conclusion
1.4. Part 3 - Other Examples
1.4.1. Swing Applet Address Book Example
1.4.2. Seam Example
1.4.3. Groovy Example
1.4.4. jBPM Example
1.4.5. ICEfaces Example
1.4.6. Java EE 6 Example
1.4.7. Swing AppFramework Example
1.4.8. Scala Example
1.4.9. GWT Client Side Example
1.4.10. GWT Hosted Mode Examples
1.4.11. Static Metawidget Example
2. Architecture
2.1. Metawidgets
2.1.1. Interface
2.1.2. Customizing Look and Feel
2.1.3. Overriding Widget Creation
2.1.4. Implementing Your Own Metawidget
2.2. Inspectors
2.2.1. Interface
2.2.2. Usage
2.2.3. CompositeInspector
2.2.4. Defaults
2.2.5. Immutability
2.2.6. inspection-result
2.2.7. Implementing Your Own Inspector (Java)
2.3. Inspection Result Processors
2.3.1. Interface
2.3.2. Usage
2.3.3. Defaults
2.3.4. Immutability
2.3.5. Implementing Your Own InspectionResultProcessor
2.4. WidgetBuilders
2.4.1. Interface
2.4.2. Usage
2.4.3. Advanced Interface
2.4.4. CompositeWidgetBuilder
2.4.5. OverriddenWidgetBuilder
2.4.6. ReadOnlyWidgetBuilder
2.4.7. Defaults
2.4.8. Immutability
2.4.9. Implementing Your Own WidgetBuilder
Special considerations for Java Server Faces
2.5. WidgetProcessors
2.5.1. Interface
2.5.2. Usage
2.5.3. Advanced Interface
2.5.4. Defaults
2.5.5. Immutability
2.5.6. Implementing Your Own WidgetProcessor
2.6. Layouts
2.6.1. Interface
2.6.2. Advanced Interface
2.6.3. LayoutDecorator
2.6.4. Usage
2.6.5. Defaults
2.6.6. Immutability
2.6.7. Implementing Your Own Layout
2.7. metawidget.xml and ConfigReader
2.7.1. Constructing New Objects
2.7.2. Calling Setter Methods
2.7.3. Constructing Primitive Types
2.7.4. Resolving Resources
2.7.5. Understanding Immutability
3. Metawidgets
3.1. Desktop Metawidgets
3.1.1. SwingMetawidget
Installation
Configuration
Customizing Look and Feel
Internationalization
Under The Hood
3.1.2. SwtMetawidget
Installation
Configuration
Customizing Look and Feel
Internationalization
3.2. Web Metawidgets
3.2.1. AngularJS Metawidget
Installation
Usage
Configuration
Customizing Look and Feel
Two-Way Binding
3.2.2. GwtMetawidget
Installation
Configuration
Reflection and Annotations
Client-Side Inspection
Customizing Look and Feel
Internationalization
3.2.3. JavaScript Metawidget
Installation
Configuration
Customizing Look and Feel
Events
Inspection Results
Web Component
3.2.4. JQuery UI Metawidget
Installation
Configuration
3.2.5. Node.js
3.2.6. HtmlMetawidgetTag (JSP)
Installation
Configuration
Customizing Look and Feel
Internationalization
Overriding Widget Creation
3.2.7. UIMetawidget (JSF)
Installation
Configuration
Customizing Look and Feel
Internationalization
Collections
Facets
JSF 2.0
3.2.8. SpringMetawidgetTag
Installation
Configuration
Customizing Look and Feel
Internationalization
Overriding Widget Creation
3.2.9. StrutsMetawidgetTag
Installation
Configuration
Customizing Look and Feel
Internationalization
Overriding Widget Creation
3.2.10. VaadinMetawidget
Installation
Configuration
3.3. Mobile Metawidgets
3.3.1. AndroidMetawidget
Installation
Configuration
Customizing Look and Feel
Internationalization
Getting and Setting Values
3.3.2. JQuery Mobile Metawidget
Installation
Configuration
3.4. Static Metawidgets
3.4.1. StaticPropertyStyle
3.4.2. StaticMetawidget
4. Inspectors
4.1. Composite Inspector
4.2. Property Inspectors
4.2.1. BaseObjectInspector
PropertyStyle
JavaBeanPropertyStyle
GroovyPropertyStyle
JavassistPropertyStyle
ScalaPropertyStyle
Implementing Your Own PropertyStyle
ActionStyle
MetawidgetActionStyle
SwingAppFrameworkActionStyle
4.2.2. PropertyTypeInspector (Java version)
4.2.3. JsonSchemaInspector (Java version)
4.2.4. JsonSchemaInspector (JavaScript version)
4.2.5. PropertyTypeInspector (JavaScript version)
4.3. Annotation Inspectors
4.3.1. BeanValidationInspector
4.3.2. FacesAnnotationInspector
4.3.3. HibernateValidatorInspector
4.3.4. JacksonInspector
4.3.5. JaxbInspector
4.3.6. JpaInspector
4.3.7. MetawidgetAnnotationInspector
4.3.8. OvalInspector
4.4. XML Inspectors
4.4.1. BaseXmlInspector
Mixing XML and Object-based Inspectors
4.4.2. CommonsValidatorInspector
4.4.3. HibernateInspector
4.4.4. PageflowInspector
4.4.5. SeamInspector
4.4.6. XmlInspector
4.4.7. XmlSchemaInspector
4.4.8. WsdlInspector
5. InspectionResultProcessors
5.1. ComesAfterInspectionResultProcessor
5.2. FacesInspectionResultProcessor
5.3. JexlInspectionResultProcessor
5.4. JspInspectionResultProcessor
5.5. RestInspectionResultProcessor
5.6. JavaToJavaScriptTypeMappingProcessor
5.7. TypeMappingInspectionResultProcessor
5.8. XmlSchemaToJavaTypeMappingProcessor
6. WidgetBuilders
6.1. Desktop WidgetBuilders
6.1.1. Swing WidgetBuilders
OverriddenWidgetBuilder
ReadOnlyWidgetBuilder
SwingWidgetBuilder
SwingXWidgetBuilder
6.1.2. SWT WidgetBuilders
OverriddenWidgetBuilder
ReadOnlyWidgetBuilder
SwtWidgetBuilder
6.2. Web WidgetBuilders
6.2.1. GWT WidgetBuilders
ExtGwtWidgetBuilder
GwtWidgetBuilder
OverriddenWidgetBuilder
ReadOnlyWidgetBuilder
6.2.2. JavaScript WidgetBuilders
HtmlWidgetBuilder
OverriddenWidgetBuilder
ReadOnlyWidgetBuilder
6.2.3. JSP WidgetBuilders
DisplayTagWidgetBuilder
HtmlWidgetBuilder
OverriddenWidgetBuilder
ReadOnlyWidgetBuilder
SpringWidgetBuilder
StrutsWidgetBuilder
6.2.4. JSF WidgetBuilders
HtmlWidgetBuilder
IceFacesWidgetBuilder
OverriddenWidgetBuilder
ReadOnlyWidgetBuilder
PrimeFacesWidgetBuilder
RichFacesWidgetBuilder
TomahawkWidgetBuilder
6.2.5. Vaadin WidgetBuilders
OverriddenWidgetBuilder
ReadOnlyWidgetBuilder
VaadinWidgetBuilder
6.3. Mobile WidgetBuilders
6.3.1. Android WidgetBuilders
AndroidWidgetBuilder
OverriddenWidgetBuilder
ReadOnlyWidgetBuilder
7. WidgetProcessors
7.1. Desktop WidgetProcessors
7.1.1. Swing WidgetProcessors
Property Binding
BeansBindingProcessor
BeanUtilsProcessor
Action Binding
7.1.2. SWT WidgetProcessors
DataBindingProcessor
ReflectionBindingProcessor
7.2. Web WidgetProcessors
7.2.1. GWT WidgetProcessors
Property Binding
Action Binding
StyleNameProcessor
7.2.2. JavaScript WidgetProcessors
AngularWidgetProcessor
BootstrapWidgetProcessor
DisabledAttributeProcessor
IdProcessor
RequiredAttributeProcessor
SimpleBindingProcessor
7.2.3. JSF WidgetProcessors
AjaxProcessor
CssStyleProcessor
HiddenFieldProcessor
ImmediateAttributeProcessor
LabelProcessor
ReadableIdProcessor
RequiredAttributeProcessor
RichFacesProcessor
StandardBindingProcessor
StandardConverterProcessor
StandardValidatorProcessor
7.2.4. JSP WidgetProcessors
HiddenFieldProcessor
7.2.5. Vaadin WidgetProcessors
CaptionProcessor
MinimumMaximumValidatorProcessor
RequiredProcessor
SimpleBindingProcessor
7.3. Mobile WidgetProcessors
7.3.1. Android WidgetProcessors
DisabledAttributeProcessor
SimpleBindingProcessor
8. Layouts
8.1. Desktop Layouts
8.1.1. Swing Layouts
BoxLayout
FlowLayout
GridBagLayout
GroupLayout
MigLayout
SeparatorLayoutDecorator
TabbedPaneLayoutDecorator
TitledPanelLayoutDecorator
8.1.2. SWT Layouts
FillLayout
GridLayout
MigLayout
RowLayout
SeparatorLayoutDecorator
TabFolderLayoutDecorator
8.2. Web Layouts
8.2.1. GWT Layouts
FlexTableLayout
FlowLayout
LabelLayoutDecorator
TabPanelLayoutDecorator
8.2.2. JavaScript Layouts
BootstrapDivLayout
DefinitionListLayout
DivLayout
HeadingTagLayoutDecorator
TabLayoutDecorator (Bootstrap)
TabLayoutDecorator (JQuery UI)
TableLayout
SimpleLayout
8.2.3. JSF Layouts
HtmlDivLayoutRenderer
HtmlTableLayoutRenderer
OutputTextLayoutDecorator
PanelGroupLayoutDecorator
PanelLayoutDecorator
PanelTabSetLayoutDecorator
SimpleTogglePanelLayoutDecorator
HtmlSimpleLayout
TabPanelLayoutDecorator
TabViewLayoutDecorator
8.2.4. JSP Layouts
HeadingTagLayoutDecorator
HtmlTableLayout
SpringTableLayout
SimpleLayout
8.2.5. Vaadin Layouts
FormLayout
HeadingTagLayoutDecorator
HorizontalLayout
TabSheetLayoutDecorator
8.3. Mobile Layouts
8.3.1. Android Layouts
LinearLayout
TableLayout
TabHostLayoutDecorator
TextViewLayoutDecorator
9. How To's
9.1. Order Properties
9.2. View The Same Object In Different Ways
9.3. Deploy As An Enterprise ARchive (EAR)
9.4. Inspect Remotely
9.4.1. Combine Remote Inspections
9.5. Improve Performance
9.5.1. JAR Size
9.5.2. Memory Usage
9.5.3. DomInspector and DomInspectionResultProcessor
9.5.4. Rebinding
10. Troubleshooting
10.1. General
10.1.1. Metawidget Doesn't Work!
10.2. Inspectors
10.2.1. General Inspectors
My Inspector does not return anything
10.2.2. Annotation-Based Inspectors
I get "java.lang.TypeNotPresentException"
My inspector is not finding my annotations
10.3. Web Metawidgets
10.3.1. I put <m:metawidget > in my page but nothing appears
10.3.2. Java Server Faces
I have annotated my getter, but Metawidget is ignoring my annotations
My parameterized List renders as a single column
I get "View could not be restored"
I get "The class ... does not have a readable property"
10.3.3. Struts
I get "Cannot find bean org.apache.struts.taglib.html.BEAN in any scope"
I see "MultipartRequestHandler", "ServletWrapper" and other weird names

Preface

Building user interfaces for domain objects can be cumbersome and time consuming in today's applications. Metawidget is an Object/User Interface Mapping tool. The term Object/User Interface mapping (OIM) refers to the technique of inspecting objects and creating User Interface (UI) widgets.

As much as possible, Metawidget does this without introducing new technologies. As shown in Figure 1, Metawidget inspects an application's existing back-end architecture (such as REST, annotations, configuration files) and creates widgets native to its existing front-end framework (such as JavaScript, Java Server Faces, Android).

Metawidget inspects existing back-ends and creates widgets native to existing front-ends

Figure 1. Metawidget inspects existing back-ends and creates widgets native to existing front-ends


Building great UIs is both art and science. Metawidget does not attempt to address the art, it only automates the science. That is to say, it does not overlap with those areas of UI design involving creativity and subjectivity - its goal is only to ease the creation of areas that are already rigidly defined. Typically, this means those areas that display data and those that collect data - these tend to be both commonplace and consistent (indeed, consistency is a desirable trait) so there is good opportunity for automation.

[Tip]Note
This User Guide and Reference Documentation is included in the Metawidget distribution as a PDF, a single HTML page or as multiple HTML pages, depending on your reading preference.

Supported Technologies

A primary goal of Metawidget is to work with your existing front-end and back-end architecture. Out of the box, Metawidget supports a broad range of both front-end and back-end technologies, and makes it easy to add your own.

Metawidget comes with a native UI component for each supported front-end. This support includes: Android, Google Web Toolkit (including extensions such as ExtGWT), 'plain' HTML 5 (POH5), JavaScript (including extensions such as AngularJS, Bootstrap, JQuery Mobile, JQuery UI and Node.js), Java Server Faces (including extensions such as Facelets, ICEfaces, PrimeFaces, RichFaces and Tomahawk), 'plain' Java Server Pages (including extensions such as DisplayTag), Spring Web MVC, Struts, Swing (including extensions such as Beans Binding, JGoodies, MigLayout and SwingX), SWT and Vaadin.

Metawidget can read domain object information from any combination of supported back-end technologies. This support includes: annotations, Bean Validation (JSR 303), Commons JEXL, Commons Validator, Groovy, Hibernate, Hibernate Validator, Jackson, JavaBeans, Java Persistence Architecture (JPA), Javassist, JBoss Forge, JBoss jBPM, JSON, JSON Schema, OVal, REST, Scala, Seam and the Swing AppFramework.

[Tip]Note
It is not a goal of Metawidget that all widgets look the same on every front-end framework, or that all back-end technologies conform to some 'lowest common denominator': every technology has different features, and Metawidget takes full advantage of this.

The next chapter presents a tutorial that covers using Metawidget with a variety of front-ends and back-ends. Chapter 2 then follows with a more in-depth architectual overview. Chapters 3-8 explore each supported front-end and back-end technology in detail. Finally, chapters 9 and 10 offer general advice and troubleshooting.

1. Introduction to Metawidget

This chapter is an introductory tutorial for new users of Metawidget. Before you begin, you need to download the binary distribution from http://metawidget.org/download.php.

If you are using a Java-based framework (including Android, and Java-based web frameworks such as GWT or Java Server Faces) you should follow Section 1.1, “Part 1 (Java version) - The First Metawidget Application”. If you are using a pure JavaScript-based framework (such as AngularJS or JQuery Mobile) you should follow Section 1.2, “Part 1 (JavaScript version) - The First Metawidget Application”.

1.1 Part 1 (Java version) - The First Metawidget Application

Part 1 starts with a simple Swing application and develops it in easy to understand steps. Metawidget supports many Java-based UI frameworks, not just Swing, but we start with Swing because it ships with Java SE and requires minimal setup.

This tutorial should take around 20 minutes. We recommend you use your preferred Java development environment. If you use an Integrated Development Environment (IDE), you will need to start a new Java project and add metawidget-all.jar to it. Otherwise, you just need to ensure metawidget-all.jar is on your CLASSPATH.

1.1.1 The Object

Metawidget is an Object/User Interface Mapping tool (OIM), so first we need an object to map from - the O in OIM. Create a class in your project called Person with the following code:

package com.myapp;

public class Person {
	private String	mName;
	private int		mAge;
	private boolean	mRetired;

	public String getName() { return mName; }
	public void setName( String name ) { mName = name; }

	public int getAge() { return mAge; }
	public void setAge( int age ) { mAge = age; }

	public boolean isRetired() { return mRetired; }
	public void setRetired( boolean retired ) { mRetired = retired; }
}

1.1.2 The Interface

Next we need a User Interface framework - the I in OIM. Create a class in your project called Main with the following code:

package com.myapp;

import javax.swing.*;
import org.metawidget.swing.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();
		
		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setToInspect( person );
		
		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}
[Tip]Note
Many IDEs include visual UI builders for dragging and dropping widgets. Metawidget integrates with these tools and Metawidgets can be dragged and dropped like any other. As we shall see, however, Metawidget widgets automatically fill themselves with child widgets at runtime, saving significant development time.

1.1.3 The Output

If you're using an IDE, such as NetBeans, your project should look something like pictured in Figure 1.1.

Metawidget tutorial in NetBeans IDE

Figure 1.1. Metawidget tutorial in NetBeans IDE


Run the code. You should see the screen in Figure 1.2.

SwingMetawidget rendering of Person class

Figure 1.2. SwingMetawidget rendering of Person class


The SwingMetawidget has automatically populated itself with child widgets at runtime. It has chosen JSpinner, JTextField and JCheckBox widgets based on the types of the properties of the Person class. This is the First Goal Of Metawidget:

[Important]First Goal Of Metawidget
Metawidget creates UI widgets by inspecting existing architectures

By default, SwingMetawidget has laid out the JComponents using java.awt.GridBagLayout. Try resizing the window, and the JComponents will resize with it. If you've ever tried using java.awt.GridBagLayout yourself, either through code or a visual UI builder, you'll know how fiddly it can be. Having Metawidget do it for you is a real time-saver.

Clearly this is not a complete UI. There are no Save or Cancel buttons, for example, and the JComponents appear uncomfortably tight to the left, top and right edges of the JFrame. This is explained by the Second Goal Of Metawidget:

[Important]Second Goal Of Metawidget
Metawidget does not try to 'own' the entire UI - it focuses on creating native sub-widgets for slotting into existing UIs

You slot Metawidget alongside your standard UI components, often combining several Metawidgets on the same screen. We'll see how this works later.

1.1.4 Ordering The Properties

Currently the name, age and retired properties are arranged alphabetically in the UI - their order does not match the way they are defined in the Person class. This is because property ordering information is not retained within Java class files (as per the Java Language Specification).

To correct this, Metawidget needs to gather additional information. There are several ways to do this (i.e. you don't have to use annotations), but the simplest for now is to use the built-in Metawidget annotation @UiComesAfter.

Annotate the Person class as shown below (lines to add are highlighted):

package com.myapp;

import org.metawidget.inspector.annotation.*;

public class Person {
	private String	mName;
	private int		mAge;
	private boolean	mRetired;

	public String getName() { return mName; }
	public void setName( String name ) { mName = name; }

	@UiComesAfter( "name" ) 1
	public int getAge() { return mAge; }
	public void setAge( int age ) { mAge = age; }

	@UiComesAfter( "age" )
	public boolean isRetired() { return mRetired; }
	public void setRetired( boolean retired ) { mRetired = retired; }
}

1

Annotations can also be applied to the private fields, such as mAge, but that requires a little extra configuration.

Run the code again. This time the properties appear in the correct order:

Properties in correct order

Figure 1.3. Properties in correct order


1.1.5 Inspectors

Introducing new annotations to improve the UI is not really in the spirit of the First Goal Of Metawidget. We'd much rather improve it by gathering information from existing sources. To demonstrate, we'll need an external source of metadata. Create a file called metawidget-metadata.xml in the same folder as your Main class:

<inspection-result xmlns="http://metawidget.org/inspection-result"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="1.0"	
	xsi:schemaLocation="http://metawidget.org/inspection-result
		http://metawidget.org/xsd/inspection-result-1.0.xsd">

	<entity type="com.myapp.Person">
		<property name="name"/>
		<property name="age"/>
		<property name="retired"/>
	</entity>

</inspection-result>

To recognise the XML file, Metawidget needs to use a different Inspector. Metawidget comes with multiple Inspectors, each targeting different sources of information. Change the Main class to use an XmlInspector (lines to add are highlighted):

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.xml.*;
import org.metawidget.swing.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();
		
		SwingMetawidget metawidget = new SwingMetawidget();
		XmlInspectorConfig config = new XmlInspectorConfig();
		config.setInputStream( Main.class.getResourceAsStream( "metawidget-metadata.xml" ));
		metawidget.setInspector( new XmlInspector( config ) );
		metawidget.setToInspect( person );
		
		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Then remove all annotations from the Person class:

package com.myapp;

public class Person {
	private String	mName;
	private int		mAge;
	private boolean	mRetired;

	public String getName() { return mName; }
	public void setName( String name ) { mName = name; }

	public int getAge() { return mAge; }
	public void setAge( int age ) { mAge = age; }

	public boolean isRetired() { return mRetired; }
	public void setRetired( boolean retired ) { mRetired = retired; }
}

Run the code again. It does not yield the correct result - the properties appear in the correct order, but the JSpinner and JCheckBox are now JTextFields! What happened?

Correct property order, but wrong JComponents

Figure 1.4. Correct property order, but wrong JComponents


Metawidget Inspectors are very targeted in what they inspect. XmlInspector looks for metadata from XML files - but it does not look for anything else, such as JavaBean property types. Before we explicitly specified an XmlInspector, Metawidget had been implictly using two Inspectors for us, called PropertyTypeInspector and MetawidgetAnnotationInspector.

1.1.6 Combining Multiple Inspection Results

What we need is to combine the results of PropertyTypeInspector, MetawidgetAnnotationInspector and XmlInspector before returning them to SwingMetawidget. We do this using CompositeInspector:

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.inspector.xml.*;
import org.metawidget.swing.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();
		
		SwingMetawidget metawidget = new SwingMetawidget();
		XmlInspectorConfig config = new XmlInspectorConfig();
		config.setInputStream( Main.class.getResourceAsStream( "metawidget-metadata.xml" ));		
		CompositeInspectorConfig inspectorConfig = new CompositeInspectorConfig().setInspectors(
			new XmlInspector( config ),
			new PropertyTypeInspector(),
			new MetawidgetAnnotationInspector() );
		metawidget.setInspector( new CompositeInspector( inspectorConfig ) );
		metawidget.setToInspect( person );
		
		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code again. This time properties appear both in the correct order and using the correct JComponent:

Using multiple inspectors

Figure 1.5. Using multiple inspectors


This idea of combining multiple Inspectors to inspect different characteristics of your existing architecture is very powerful. Metawidget comes with pre-written Inspectors for many different technologies - from JPA and Hibernate Validator annotations, to struts-config.xml configuration files, to Groovy and Scala properties. There is a lot of metadata already lurking in back-end systems - it just needs extracting. For example, JpaInspector understands this...

import javax.persistence.Column;

public class Person {
	@Column( nullable = false )
	public String getName() { ... };
}

...denotes name is a required property (could be rendered with an asterisk after it in the UI). Equally, PropertyTypeInspector understands that...

public class Person {
	private String mName;
	
	public String getName() {
		return mName;
	}
	
	// No setter
}

...signifies name is a read-only property (could be rendered as a label in the UI).

1.1.7 Controlling The Layout

There are several ways to control the layout of the components. To demonstrate, Try adding the following properties to the Person class:

package com.myapp;

import org.metawidget.inspector.annotation.*;

public class Person {

	public enum Gender { Male, Female }
	
	private String	mName;
	private int		mAge;
	private boolean	mRetired;
	private Gender	mGender;
	private String	mNotes;
	private String	mEmployer;
	private String	mDepartment;
	
	public String getName() { return mName; }
	public void setName( String name ) { mName = name; }

	public int getAge() { return mAge; }
	public void setAge( int age ) { mAge = age; }

	public boolean isRetired() { return mRetired; }
	public void setRetired( boolean retired ) { mRetired = retired; }

	@UiComesAfter( "retired" ) 1
	public Gender getGender() { return mGender; }
	public void setGender( Gender gender ) { mGender = gender; }

	@UiComesAfter( "gender" )
	@UiLarge
	public String getNotes() { return mNotes; }
	public void setNotes( String notes ) { mNotes = notes; }

	@UiComesAfter( "notes" )
	@UiSection( "Work" )
	public String getEmployer() { return mEmployer; }
	public void setEmployer( String employer ) { mEmployer = employer; }

	@UiComesAfter( "employer" )
	public String getDepartment() { return mDepartment; }
	public void setDepartment( String department ) { mDepartment = department; }
}

1

We'll use annotations again for brevity. But such metadata could be inspected from any source, such as an XML file.

This code produces the screen in Figure 1.6. Annotations have been used to define section headings and 'large' properties (i.e. a JTextArea).

Additional properties and a section heading

Figure 1.6. Additional properties and a section heading


[Tip]Note
For a list of all the annotations MetawidgetAnnotationInspector recognises, see Section 4.3.7, “MetawidgetAnnotationInspector”.

By default, SwingMetawidget lays out JComponents using org.metawidget.swing.layout.GridBagLayout. You can configure this layout, or swap it for a different layout, using SwingMetawidget.setMetawidgetLayout. Modify the code to use a GridBagLayout with 2 columns:

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.inspector.xml.*;
import org.metawidget.swing.*;
import org.metawidget.swing.layout.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();
		
		SwingMetawidget metawidget = new SwingMetawidget();
		XmlInspectorConfig config = new XmlInspectorConfig();
		config.setInputStream( Main.class.getResourceAsStream( "metawidget-metadata.xml" ));		
		CompositeInspectorConfig inspectorConfig = new CompositeInspectorConfig().setInspectors(
			new XmlInspector( config ),
			new PropertyTypeInspector(),
			new MetawidgetAnnotationInspector() );
		metawidget.setInspector( new CompositeInspector( inspectorConfig ) );
		GridBagLayoutConfig nestedLayoutConfig = new GridBagLayoutConfig().setNumberOfColumns( 2 ); 
		SeparatorLayoutDecoratorConfig layoutConfig = new SeparatorLayoutDecoratorConfig().setLayout(
			new org.metawidget.swing.layout.GridBagLayout( nestedLayoutConfig ));
		metawidget.setMetawidgetLayout( new SeparatorLayoutDecorator( layoutConfig ));
		metawidget.setToInspect( person );
		
		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The JComponents are now arranged across two columns:

A two-column layout

Figure 1.7. A two-column layout


You may have noticed the GridBagLayout is nested inside a SeparatorLayoutDecorator. This is responsible for separating widgets in different sections using JSeparators. However there are other choices for separating widgets. Modify the code to use TabbedPaneLayoutDecorator instead:

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.inspector.xml.*;
import org.metawidget.swing.*;
import org.metawidget.swing.layout.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();
		
		SwingMetawidget metawidget = new SwingMetawidget();
		XmlInspectorConfig config = new XmlInspectorConfig();
		config.setInputStream( Main.class.getResourceAsStream( "metawidget-metadata.xml" ));		
		CompositeInspectorConfig inspectorConfig = new CompositeInspectorConfig().setInspectors(
			new XmlInspector( config ),
			new PropertyTypeInspector(),
			new MetawidgetAnnotationInspector() );
		metawidget.setInspector( new CompositeInspector( inspectorConfig ) );
		GridBagLayoutConfig nestedLayoutConfig = new GridBagLayoutConfig().setNumberOfColumns( 2 ); 
		TabbedPaneLayoutDecoratorConfig layoutConfig = new TabbedPaneLayoutDecoratorConfig().setLayout(
			new org.metawidget.swing.layout.GridBagLayout( nestedLayoutConfig ));
		metawidget.setMetawidgetLayout( new TabbedPaneLayoutDecorator( layoutConfig ));
		metawidget.setToInspect( person );
		
		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The section heading is now a JTabbedPane:

Two-column layout with a JTabbedPane

Figure 1.8. Two-column layout with a JTabbedPane


Again, if you've ever used java.awt.GridBagLayout by hand, you'll appreciate how much easier Metawidget makes it to apply consistent layouts across all screens of your application.

1.1.8 Controlling Widget Creation

There are several ways to control widget creation. One way is to drop child controls inside the SwingMetawidget. This approach works well both programmatically within code and within visual UI builders.

Modify the code to add a JComboBox to the Metawidget:

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.inspector.xml.*;
import org.metawidget.swing.*;
import org.metawidget.swing.layout.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();
		
		SwingMetawidget metawidget = new SwingMetawidget();
		XmlInspectorConfig config = new XmlInspectorConfig();
		config.setInputStream( Main.class.getResourceAsStream( "metawidget-metadata.xml" ));		
		CompositeInspectorConfig inspectorConfig = new CompositeInspectorConfig().setInspectors(
			new XmlInspector( config ),
			new PropertyTypeInspector(),
			new MetawidgetAnnotationInspector() );
		metawidget.setInspector( new CompositeInspector( inspectorConfig ) );
		GridBagLayoutConfig nestedLayoutConfig = new GridBagLayoutConfig().setNumberOfColumns( 2 ); 
		TabbedPaneLayoutDecoratorConfig layoutConfig = new TabbedPaneLayoutDecoratorConfig().setLayout(
			new org.metawidget.swing.layout.GridBagLayout( nestedLayoutConfig ));
		metawidget.setMetawidgetLayout( new TabbedPaneLayoutDecorator( layoutConfig ));
		metawidget.setToInspect( person );
		JComboBox combo = new JComboBox();
		combo.setName( "retired" );
		metawidget.add( combo );
		
		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The JComboBox appears in place of the retired JCheckBox, because it has the same name (i.e. 'retired') as Metawidget would have given the JCheckbox:

The 'retired' property has been overridden

Figure 1.9. The 'retired' property has been overridden


[Tip]Note
The default algorithm looks for child controls with the same name, but you can plug in your own implementation if you need to. See Section 2.4.5, “OverriddenWidgetBuilder”.

To suppress a widget's creation entirely, simply supplying an empty JPanel named 'retired' will not work as Metawidget will still create an accompanying label in the left hand column. Instead, Metawidget includes special Stub widgets for this purpose:

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.inspector.xml.*;
import org.metawidget.swing.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();
		
		SwingMetawidget metawidget = new SwingMetawidget();
		XmlInspectorConfig config = new XmlInspectorConfig();
		config.setInputStream( Main.class.getResourceAsStream( "metawidget-metadata.xml" ));		
		CompositeInspectorConfig inspectorConfig = new CompositeInspectorConfig().setInspectors(
			new XmlInspector( config ),
			new PropertyTypeInspector(),
			new MetawidgetAnnotationInspector() );
		metawidget.setInspector( new CompositeInspector( inspectorConfig ) );
		GridBagLayoutConfig nestedLayoutConfig = new GridBagLayoutConfig().setNumberOfColumns( 2 ); 
		TabbedPaneLayoutDecoratorConfig layoutConfig = new TabbedPaneLayoutDecoratorConfig().setLayout(
			new org.metawidget.swing.layout.GridBagLayout( nestedLayoutConfig ));
		metawidget.setMetawidgetLayout( new TabbedPaneLayoutDecorator( layoutConfig ));
		metawidget.setToInspect( person );
		metawidget.add( new Stub( "retired" ));
		
		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The retired property and its label will not appear:

The 'retired' property has been suppressed

Figure 1.10. The 'retired' property has been suppressed


Another way is to use a @UiHidden annotation on the business class:

package com.myapp;

import org.metawidget.inspector.annotation.*;

public class Person {

	public enum Gender { Male, Female }
	
	private String	mName;
	private int		mAge;
	private boolean	mRetired;
	private Gender	mGender;
	private String	mNotes;
	private String	mEmployer;
	private String	mDepartment;
	
	public String getName() { return mName; }
	public void setName( String name ) { mName = name; }

	public int getAge() { return mAge; }
	public void setAge( int age ) { mAge = age; }

	@UiHidden
	public boolean isRetired() { return mRetired; }
	public void setRetired( boolean retired ) { mRetired = retired; }

	@UiComesAfter( "retired" )
	public Gender getGender() { return mGender; }
	public void setGender( Gender gender ) { mGender = gender; }

	@UiComesAfter( "gender" )
	@UiLarge
	public String getNotes() { return mNotes; }
	public void setNotes( String notes ) { mNotes = notes; }

	@UiComesAfter( "notes" )
	@UiSection( "Work" )
	public String getEmployer() { return mEmployer; }
	public void setEmployer( String employer ) { mEmployer = employer; }

	@UiComesAfter( "employer" )
	public String getDepartment() { return mDepartment; }
	public void setDepartment( String department ) { mDepartment = department; }
}

In both cases, org.metawidget.swing.layout.GridBagLayout is smart enough to always give large JComponents like notes the full width of the JFrame.

1.1.9 Configuring Metawidget Externally

So far we have been instantiating our Inspectors and Layouts in Java code. Whilst this approach is possible for all Inspectors and Layouts, many UI frameworks employ visual UI builders or intermediate languages (such as JSP) that make getting to the Java code cumbersome (i.e. you have to derive custom widgets).

As an alternative, Metawidget supports external XML configuration. Create a file called metawidget.xml in the same folder as your Main class:

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="1.0"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

	<swingMetawidget xmlns="java:org.metawidget.swing">		
		<inspector>
			<compositeInspector xmlns="java:org.metawidget.inspector.composite"
				config="CompositeInspectorConfig">
				<inspectors>
					<array>
						<xmlInspector xmlns="java:org.metawidget.inspector.xml" config="XmlInspectorConfig">
							<inputStream>
								<resource>com/myapp/metawidget-metadata.xml</resource>
							</inputStream>
						</xmlInspector>
						<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
						<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation" />
					</array>
				</inspectors>
			</compositeInspector>
		</inspector>
		<metawidgetLayout>
			<tabbedPaneLayoutDecorator xmlns="java:org.metawidget.swing.layout"
				config="TabbedPaneLayoutDecoratorConfig">
				<layout>
					<gridBagLayout config="GridBagLayoutConfig">
						<numberOfColumns>
							<int>2</int>
						</numberOfColumns>
					</gridBagLayout>
				</layout>
			</tabbedPaneLayoutDecorator>
		</metawidgetLayout>	
	</swingMetawidget>

</metawidget>

Now update your Main class to use this file:

package com.myapp;

import javax.swing.*;
import org.metawidget.swing.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setConfig( "com/myapp/metawidget.xml" );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The output is the same as before, but this time we are configuring our Metawidget via external XML.

Visual UI builders can call SwingMetawidget.setConfig from the builder, with no coding required. Other UI frameworks (e.g. JSPs, Android) have similar 'code free' approaches (e.g. setting an attribute on a JSP tag, setting an attribute in an Android layout file) to setting the XML file. We shall look at these sorts of frameworks in Part 2.

Now skip to Section 1.3, “Part 2 - The Address Book Application”

1.2 Part 1 (JavaScript version) - The First Metawidget Application

Part 1 starts with a pure JavaScript application and develops it in easy to understand steps. Metawidget supports many JavaScript-based UI frameworks, but we start with pure JavaScript because it requires minimal setup.

This tutorial should take around 20 minutes. We recommend you use your preferred JavaScript development environment.

1.2.1 The Object

Metawidget is an Object/User Interface Mapping tool (OIM), so first we need an object to map from - the O in OIM. Create a new HTML page in your project called index.html with the following code:

<!DOCTYPE html>
<html>
	<head>
		<script type="text/javascript">
			var person = {
				name: "Homer Simpson",
				age: 40,
				retired: false
			};
		</script>
	</head>
	<body>
	</body>
</html>

1.2.2 The Interface

Next we need a User Interface - the I in OIM. Add the following code to your HTML page (lines to add are highlighted):

<!DOCTYPE html>
<html>
	<head>
		<script src="lib/metawidget/core/metawidget-core.min.js" type="text/javascript"></script>
		<script type="text/javascript">
			var person = {
				name: "Homer Simpson",
				age: 40,
				retired: false
			};
		</script>
	</head>
	<body>
		<div id="metawidget"></div>
		<script type="text/javascript">
			var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ));
			mw.toInspect = person;
			mw.buildWidgets();
		</script>
	</body>
</html>

You will also need to copy js/lib/metawidget/core/metawidget-core.min.js from the Metawidget binary distribution into your project.

1.2.3 The Output

Open your HTML page in your browser. You should see the page in Figure 1.11.

Metawidget rendering of person object

Figure 1.11. Metawidget rendering of person object


Metawidget has automatically populated the div with child widgets at runtime. It has chosen text, number and checkbox inputs based on the types of the properties of the person object. This is the First Goal Of Metawidget:

[Important]First Goal Of Metawidget
Metawidget creates UI widgets by inspecting existing architectures

By default, Metawidget has laid out the HTML input components using a table. This may not be your preferred approach, and either way this is clearly not a complete UI. There are no Save or Cancel buttons, for example. This is explained by the Second Goal Of Metawidget:

[Important]Second Goal Of Metawidget
Metawidget does not try to 'own' the entire UI - it focuses on creating native sub-widgets for slotting into existing UIs

You slot Metawidget alongside your standard UI components, often combining several Metawidgets on the same screen. We'll see how this works later.

1.2.4 Inspectors

Suppose we want the name property to be a required field. Such metadata cannot be represented using standard JavaScript Object Notation (JSON) so Metawidget needs a way to gather the additional information. There are several ways to do this, but the simplest for now is to add a custom Inspector (text to add is highlighted) that returns a JSON Schema:

var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
	
	inspector: function( toInspect, type, names ) {

		return {
			properties: {
				name: {
					type: "string",
					required: true
				}
			}
		};
	}
} );

Refresh the page in your browser. It does not yield the correct result - the name property appears as a required field, but the age and retired properties have disappeared! What happened?

Name is required, but other properties are missing

Figure 1.12. Name is required, but other properties are missing


Metawidget Inspectors are very targeted in what they inspect. Our custom Inspector returns information about name being a required field - but it does not return anything else, such as properties from our JSON object. Before we explicitly specified a new Inspector, Metawidget had been implictly using another Inspector for us, called PropertyTypeInspector.

1.2.5 Combining Multiple Inspection Results

What we need is to combine the results of PropertyTypeInspector and our custom Inspector before returning them to the Metawidget. We do this using CompositeInspector:

var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
	
	inspector: new metawidget.inspector.CompositeInspector( [ new metawidget.inspector.PropertyTypeInspector(),
		function( toInspect, type, names ) {
		
				return {
					properties: {					
						name: {
							type: "string", 1
							required: true
						}
					}
				};
			}
		}
	] )
} );

1

type can now be removed from our custom Inspector, as it will be looked up by PropertyTypeInspector.

Refresh the page again. This time all properties appear and name is marked as a required field.

Using multiple inspectors

Figure 1.13. Using multiple inspectors


This idea of combining multiple Inspectors to inspect different characteristics of your existing architecture is very powerful. Metawidget comes with pre-written Inspectors for many different technologies and makes it easy to add your own, such as for REST services (see Section 5.5, “RestInspectionResultProcessor”). There is a lot of metadata already lurking in back-end systems - it just needs extracting.

1.2.6 Controlling The Layout

There are several ways to control the layout of the components. To demonstrate, Try adding the following metadata for the person object:

function( toInspect, type, names ) {
		
	return {
		properties:
			name: {
				required: true
			},
			notes: {
				type: "string",
				large: true 1
			},
			employer: {
				type: "string",
				section: "Work"
			},
			department: {
				type: "string"
			}
		}
	};
}

1

Metawidget supports a superset of JSON Schema (v3). Metadata such as large and section are not part of the JSON Schema specification, but are useful for UIs.

This produces the screen in Figure 1.14. Metadata has been added to define section headings and 'large' properties (i.e. a textarea).

Additional properties and a section heading

Figure 1.14. Additional properties and a section heading


By default, Metawidget lays out components using an HTML table. You can configure this layout or swap it for a different one. Modify the code as follows:

var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
	
	inspector: new metawidget.inspector.CompositeInspector( [ new metawidget.inspector.PropertyTypeInspector(),
		function( toInspect, type, names ) {
				
			return {
				properties:
					name: {
						required: true
					},
					notes: {
						type: "string",
						large: true
					},
					employer: {
						type: "string",
						section: "Work"
					},
					department: {
						type: "string"
					}
				}
			};
		} ] ),
		
	layout: new metawidget.layout.HeadingTagLayoutDecorator(
		new metawidget.layout.TableLayout( { numberOfColumns: 2 } ))
} );

Refresh the page. The components are now arranged across two columns:

A two-column layout

Figure 1.15. A two-column layout


[Tip]Note
Your browser may not have stretched the textarea across the full width of the table, even though the colspan has been set to 4. You can add <style>textarea { width: 100% }</style> into the head to confirm this visually

You may have noticed the TableLayout is nested inside a HeadingTagLayoutDecorator. This is responsible for separating widgets in different sections using h1 tags. However there are other choices for separating widgets. Modify the code to use a JQuery UI-based TabLayoutDecorator instead:

<!DOCTYPE html>
<html>
	<head>
		<script src="lib/metawidget/core/metawidget-core.min.js" type="text/javascript"></script>	
		<link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
		<script src="http://code.jquery.com/jquery-1.8.3.js"></script>
		<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
		<script src="lib/metawidget/jquery-ui/metawidget-jqueryui.min.js" type="text/javascript"></script>
		<script type="text/javascript">
			var person = {
				name: "Homer Simpson",
				age: 40,
				retired: false
			};
		</script>
	</head>
	<body>
		<form>
			<div id="metawidget"></div>
		</form>
		<script type="text/javascript">
			var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
				
				inspector: new metawidget.inspector.CompositeInspector( [ new metawidget.inspector.PropertyTypeInspector(),
					function( toInspect, type, names ) {
							
						return {
							properties:
								name: {
									required: true
								},
								notes: {
									type: "string",
									large: true
								},
								employer: {
									type: "string",
									section: "Work"
								},
								department: {
									type: "string"
								}
							}
						};
					} ] ),
					
				layout: new metawidget.jqueryui.layout.TabLayoutDecorator(
					new metawidget.layout.TableLayout( { numberOfColumns: 2 } ))
			} );		
			mw.toInspect = person;
			mw.buildWidgets();
		</script>
	</body>
</html>

You will also need to copy js/lib/metawidget/jquery-ui/metawidget-jqueryui.min.js from the Metawidget binary distribution into your project.

Refresh the page. The section heading is now a tab:

Two-column layout with a JQuery UI tab

Figure 1.16. Two-column layout with a JQuery UI tab


Metawidget makes it easy to apply consistent layouts across all pages of your application.

1.2.7 Controlling Widget Creation

There are several ways to control widget creation. One way is to add child controls inside the Metawidget:

<form>
	<div id="metawidget">
		<select id="retired">
			<option />
			<option>true</option>
			<option>false</option>
		</select>
	</div>
</form>

Refresh the page. The select box appears in place of the checkbox, because it has the same id (i.e. 'retired') as Metawidget would have given the checkbox. Notice Metawidget has still bound its value:

The 'retired' property has been overridden

Figure 1.17. The 'retired' property has been overridden


[Tip]Note
The default algorithm looks for child controls with the same id, but you can plug in your own implementation if you need to. See Section 2.4.5, “OverriddenWidgetBuilder”.

To suppress a widget's creation entirely, simply supplying an empty div with id 'retired' will not work as Metawidget will still create an accompanying label in the left hand column. Instead, Metawidget includes special stub widgets for this purpose:

<form>
	<div id="metawidget">
		<stub id="retired"></stub>
	</div>
</form>

Refresh the page. The retired property and its label will not appear:

The 'retired' property has been suppressed

Figure 1.18. The 'retired' property has been suppressed


Another way is to add a hidden attribute to the metadata:

function( toInspect, type, names ) {
	return {
		properties:
			name: {
				required: true
			},
			retired: {
				hidden: true
			},			
			notes: {
				type: "string",
				large: true
			},
			employer: {
				type: "string",
				section: "Work"
			},
			department: {
				type: "string"
			}
		}
	};
}

In both cases, metawidget.layout.TableLayout is smart enough to always give large components like textarea the full colspan of the table.

1.3 Part 2 - The Address Book Application

Part 2 explores a more substantial application, and shows how Metawidget can be used to map the same back-end to multiple front-ends. We will develop an Address Book application with desktop-based, Web-based and mobile-based UIs.

This tutorial should take around 45 minutes. Before you begin, you will need to download the examples distribution from http://metawidget.org/download.php. This includes pre-built example applications with full source code. To save time, we will not focus on any one front-end framework in detail. For detailed framework-specific instructions, please see Chapter 3, Metawidgets.

1.3.1 Desktop Address Book

The Desktop Address Book is essentially a larger version of the application developed in Part 1 - it just has more domain objects and more widgets.

[Tip]Note
If you are only interested in the JavaScript-based Metawidget, you should skip straight to Section 1.3.2, “Web Address Book”.

The application is pre-built for you in examples/swing/addressbook-swing.jar. This is a self-executing JAR. For convenience, it has MANIFEST.MF dependencies hard-coded into it, so it's best not to move it to a different folder (if you do, you'll need to manually fix up your CLASSPATH).

[Tip]Note
There is also an SWT version of the Address Book sample in examples/swt/addressbook-swt.jar. You can mostly replace references to 'Swing' in this section with 'SWT' and follow along using SWT if preferred.

Run the code by navigating to the examples/swing folder and typing:

java -jar addressbook-swing.jar

The opening screen displays a search filter (at the top) and lists existing Address Book entries (at the bottom) as in Figure 1.19 (Swing version) and Figure 1.20 (SWT version).

Swing Desktop Address Book opening screen

Figure 1.19. Swing Desktop Address Book opening screen


SWT Desktop Address Book opening screen

Figure 1.20. SWT Desktop Address Book opening screen


The three search filter fields (Firstname, Surname and Type) are created by SwingMetawidget based on the ContactSearch business class. This includes populating the Type dropdown based on the ContactType enum. The Search, Add Personal Contact and Add Business Contact buttons are created by SwingMetawidget based on annotated action methods in the ContactDialog class. The rest of the screen - including the images, background and layout - are managed by regular Swing code and are not controlled by Metawidget: Metawidget does not try to 'own' the entire UI.

[Tip]Note
Full source code for all the examples, such as the code for the ContactSearch, ContactType and ContactDialog classes, is included under the src/examples folder of the examples distribution.

Click Add Personal Contact. The screen displays a form for filling out Personal Contact information as in Figure 1.21.

Desktop Address Book 'Add Personal Contact' screen

Figure 1.21. Desktop Address Book 'Add Personal Contact' screen


All the form fields are created by SwingMetawidget based on the PersonalContact business class. This class is itself derived from the Contact business class. It includes some Metawidget annotations for dropdown values and section headings.

Note the code only has one JDialog class (ContactDialog), but is capable of supporting both PersonalContact and BusinessContact UIs. The fields in the UI change depending on the object passed to ContactDialog at runtime. This is the Third Goal Of Metawidget:

[Important]Third Goal Of Metawidget
Metawidget can perform inspection at runtime, detecting types and subtypes dynamically

The Address property is created as a nested SwingMetawidget. This is the default behaviour when Metawidget encounters datatypes it does not know how to represent using any other UI widget. The Communications property has been overridden with a manually specified JTable.

In addition, JTable.setCellEditor uses SwingMetawidget to render single JComponents as CellEditors. This includes automatically populating dropdown values.

Read-Only Mode

The Desktop Address Book uses Metawidget's setReadOnly(true) method to display read-only screens. Return to the main screen, and double-click on an existing contact (such as Homer Simpson). The same ContactDialog is used, but this time all the widgets are read-only labels as in Figure 1.22.

Desktop Address Book read-only mode

Figure 1.22. Desktop Address Book read-only mode


Click Edit. The labels are transformed into editable widgets by using Metawidget's setReadOnly(false), as in Figure 1.23.

Desktop Address Book edit mode

Figure 1.23. Desktop Address Book edit mode


Binding

The data from the PersonalContact object is automatically inserted into the JComponents. It is also automatically saved back when clicking Save.

Swing does not define a JComponent to Object mapping mechanism, so by default SwingMetawidget only supplies setValue and getValue methods for manually fetching values. This situation is no worse than a normal Swing application, but Metawidget can do better.

SwingMetawidget directly supports third-party binding alternatives such as Apache BeanUtils and Beans Binding (JSR 295) via SwingMetawidget.addWidgetProcessor. These binding implementations automatically map JComponent values to Object values, including performing the necessary conversions, further reducing the amount of boilerplate code required.

SwtMetawidget does something similar using org.eclipse.core.databinding.Binding.

Localization

All text within the application has been localized to the org.metawidget.example.shared.addressbook.resource.Resources resource bundle. Text created manually (such as the buttons) uses typical Swing localization code (e.g. bundle.getString). Text created by SwingMetawidget uses SwingMetawidget.setBundle, which internally defers to Swing's bundle.getString.

Localization is very easy with Metawidget. For property names, if no resource bundle is supplied, Metawidget uses an 'uncamel-cased' version of the name (e.g. dateOfBirth becomes Date Of Birth). If a bundle is supplied, Metawidget uses the property name as the bundle key. For section headings, if a bundle is supplied, Metawidget uses a 'camel-cased' version of the headings as the key.

This means developers can initially build their UIs without worrying about resource bundles, then turn on localization support later.

1.3.2 Web Address Book

As there are a large number of Web application frameworks to choose from, this example comes written in nine of the most popular: AngularJS, Google Web Toolkit (GWT), Java Server Faces (JSF) 1.x and 2.x (using Facelets), Java Server Pages (JSP), JQuery Mobile, Spring Web MVC, Struts, Vaadin and Web Components. We recommend you follow along using the one most relevant to you.

Web-based applications are inherently more difficult to setup and run than desktop-based applications because they require a server (this is true even for the pure client-side, JavaScript-based versions of the Address Book as they make REST calls). For this tutorial we recommend using Apache Tomcat as it is one of the easier containers to get running (the examples are tested against Tomcat 6.0.29). Tomcat can be downloaded from http://tomcat.apache.org.

Take a fresh install of Tomcat. The Address Book application is pre-built for you in either examples/js/angular/addressbook, examples/js/jquery/mobile/addressbook, examples/js/webcomponent/addressbook, examples/java/faces/addressbook-faces.war, examples/java/gwt/addressbook-gwt.war, examples/java/jsp/addressbook-jsp.war, examples/java/spring/addressbook-spring.war, examples/java/struts/addressbook-struts.war or examples/java/vaadin/addressbook-struts.war. Alternatively you can build it yourself by changing to the src/examples folder and typing:

mvn -pl org.metawidget.examples.faces:addressbook-faces -am integration-test

(replacing faces with gwt, jsp, spring, struts or vaadin as appropriate).

[Tip]Note
For most web environments, deploying Metawidget is as simple as adding metawidget-all.jar to WEB-INF/lib or metawidget-core.min.js to lib/metawidget. For GWT, you'll also need to include metawidget-all.jar and additional/gwt/metawidget-all-sources.jar in the CLASSPATH during your GWTCompiler step.

Copy the application into Tomcat's webapps folder, start Tomcat, and open a Web browser to http://localhost:8080/addressbook-faces. The home page displays a search filter (at the top) and lists existing Address Book entries (at the bottom) as in Figure 1.24.

Web Address Book opening screen

Figure 1.24. Web Address Book opening screen


Web Address Book opening screen (JQuery Mobile version)

Figure 1.25. Web Address Book opening screen (JQuery Mobile version)


As with the Desktop Address Book, the search filter fields are created by Metawidget (this time AngularMetawidget, UIMetawidget, GwtMetawidget, HtmlMetawidgetTag, JQueryMobileMetawidget, SpringMetawidgetTag, StrutsMetawidgetTag or VaadinMetawidget) based on the ContactSearch business class:

<m:metawidget value="#{contact.search}">
	...
</m:metawidget>

Again, this includes populating the Type dropdown and localizing the text. The Search, Add Personal Contact and Add Business Contact buttons are either manually specified in the JSP page (for GWT, Spring and Struts) or created by UIMetawidget based on annotated methods in the ContactBean (for JSF).

[Tip]Note
As with the Desktop Address Book, full source code for the examples can be found under src/examples.

The look of the Web page relies entirely on HTML and CSS technologies. The CSS classes to use are configured in metawidget.xml (or services.js for AngularJS):

<htmlMetawidget>
<parameter>
	<string>tableStyleClass</string>
	<string>table-form</string>
</parameter>
<parameter>
	<string>columnClasses</string>
	<string>table-label-column,table-component-column,table-required-column</string>
</parameter>
...

Only the layout of 'one column for the label, one column for the widget' is dictated by Metawidget, and that is again pluggable and configurable.

Click Add Personal Contact. The page displays a form for filling out Personal Contact information as in Figure 1.26.

Web Address Book 'Add Personal Contact' screen

Figure 1.26. Web Address Book 'Add Personal Contact' screen


All the form fields are created by Metawidget based on the PersonalContact business class. The section headings are the same, but have this time been rendered as HTML.

The Address property is a nested Metawidget. The Communications property has been overridden in the page with a manually specified table. UIMetawidget understands a manually-specified widget to override an automatic one if it has the same value binding as the automatic widget would have (AngularMetawidget, GwtMetawidget, SpringMetawidgetTag, StrutsMetawidgetTag and VaadinMetawidget do something similar):

<m:metawidget value="#{contact.current}">
	...		
	<h:dataTable value="#{contact.current.communications}">
		...
	</h:dataTable>						
	...							
<m:metawidget>

JSF has built-in support for executing actions on table rows. In order to use it, however, the Set returned by Contact.getCommunications must be wrapped into a DataModel. This is handled by ContactController.getCurrentCommunications, but this presents a problem: the mapping for the HtmlDataTable must be #{contact.currentCommunications}, but the mapping required to override UIMetawidget's automatic widget creation is #{contact.current.communications}.

UIMetawidget supplies UIStub for these situations. Stubs have a binding, but do nothing with it and render nothing. They can be used either to suppress widget creation entirely (a stub with an empty body) or to replace the automatic widget creation with one or more other widgets with different bindings:

<m:metawidget value="#{contact.current}">
	...		
	<m:stub value="#{contact.current.communications}">
		<h:dataTable value="#{contact.currentCommunications}">
			...
		</h:dataTable>						
	</m:stub>						
	...							
<m:metawidget>

JSP, Spring, Struts lack some component-based features found in Swing and JSF. Specifically, whilst it is possible for tags to reference their parent (using TagSupport.findAncestorWithClass), they have no way to interrogate their children. Therefore, it is not possible to directly support arbitrary child tags within HtmlMetawidget, SpringMetawidgetTag and StrutsMetawidgetTag.

Instead, we wrap the overridden Communications property in Metawidget's Stub tag. Metawidget and its Stub tags have explicit support for co-ordinating the overriding of widget creation:

<m:metawidget property="contactForm">
	...		
	<m:stub property="communications">
		<table class="data-table">
			...
		</table>
	</m:stub>						
	...							
<m:metawidget>

GwtMetawidget uses stubs around GWT widgets like FlexTable, but can use the overriding widget directly if it supports the HasName interface (e.g. TextBox, CheckBox, etc) and has a name matching the name of the domain object property.

Mixing Metawidgets

The section is specific to Spring/Struts.

Within the Communications table, implementing Add Communication calls for a design decision. Struts does not support multiple ActionForms per Action, so we are unable to combine PersonalContactForm with a CommunicationForm (as we did in the JSF). Spring has a similar limitation of not supporting multiple commandNames per form. Instead, we need to either:

  • add properties from Communication to PersonalContactForm, and ignore them when saving the PersonalContact; or

  • output plain HTML tags (i.e. independent of Spring and Struts) and handle them manually

Both approaches would be valid. For this tutorial, we choose the latter as it allows us to introduce HtmlMetawidget (a Metawidget for plain HTML/JSP webapps that don't use Struts or Spring) and demonstrate mixing two Metawidgets on the same page:

<m:metawidget property="contactForm">
	...
	<m:stub property="communications">
		<table class="data-table">
		...
		<tr>
			<jsp:useBean id="communication"
							class="org.metawidget.example.shared.addressbook.model.Communication"/>		
			<td><mh:metawidget value="communication.type" style="width: 100%" /></td>
			<td><mh:metawidget value="communication.value" style="width: 100%" /></td>
		</tr>
		...
		</table>
	</m:stub>
	...
</m:metawidget>

The two different tag prefixes m: and mh: denote different tag libraries. HtmlMetawidget is very similiar to StrutsMetawidgetTag, but has to use jsp:useBean to manually instantiate the bean (rather than letting Struts do it). Within metawidget.xml, the default Layout for HtmlMetawidget has been set to org.metawidget.jsp.tagext.layout.SimpleLayout (i.e. a plain layout, without a label column)

Expression Based Lookups

This section does not apply to GWT.

In the Desktop Address Book, the title dropdown was populated by a static lookup attribute in metawidget-metadata.xml. JSP and JSF-based technologies can do better, because they have a built-in scope-based component model and Expression Language.

Contact.getTitle is annotated using @UiFacesLookup and @UiSpringLookup (and ContactForm.getTitle is annotated using @UiStrutsLookup). These are used at runtime to create dynamic lookups.

These annotations, unlike the ones we have used so far, are UI-framework specific so you may prefer to declare them in metawidget-metadata.xml. Before doing so, however, you should understand we are still not introducing runtime dependencies into our business classes: an important feature of annotations is they 'fall away gracefully' if their implementing class is not found. Annotations never throw ClassDefNotFoundError.

Alternate Widget Libraries (JSF 1.x)

This section is specific to JSF 1.x.

Metawidget factors all widget creation into WidgetBuilders. Like Inspectors, multiple WidgetBuilders can be combined using a CompositeWidgetBuilder to support third-party component libraries. In this section we will override Metawidget's default and introduce a RichFacesWidgetBuilder alongside the standard JSF HtmlWidgetBuilder.

Go into Tomcat's webapps/addressbook-faces folder (the exploded WAR) and edit WEB-INF/metawidget.xml:

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="1.0"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
	...
	<inspector>		
		<compositeInspector xmlns="java:org.metawidget.inspector.composite"
								config="CompositeInspectorConfig">
			<inspectors>
			<array>
				<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
				<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"/>
				<facesAnnotationInspector xmlns="java:org.metawidget.inspector.faces"/>			
				<xmlInspector xmlns="java:org.metawidget.inspector.xml" config="XmlInspectorConfig"/>
			</array>
			</inspectors>
		</compositeInspector>
	</inspector>
	<widgetBuilder>
		<compositeWidgetBuilder xmlns="java:org.metawidget.widgetbuilder.composite"
			config="CompositeWidgetBuilderConfig">
			<widgetBuilders>
				<array>
					<overriddenWidgetBuilder xmlns="java:org.metawidget.faces.component.widgetbuilder"/>
					<readOnlyWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
					<richFacesWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder.richfaces"/>
					<htmlWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
				</array>
			</widgetBuilders>
		</compositeWidgetBuilder>
	</widgetBuilder>
	<layout>
		<outputTextLayoutDecorator xmlns="java:org.metawidget.faces.component.html.layout"
			config="OutputTextLayoutDecoratorConfig">
			<layout>
				<simpleLayout xmlns="java:org.metawidget.faces.component.layout"/>
			</layout>
			<styleClass>
				<string>section-heading</string>
			</styleClass>
		</outputTextLayoutDecorator>
	</layout>	
</htmlMetawidget>

</metawidget>

Now restart Tomcat, refresh your Web browser and click on Homer Simpson. Notice how the Date of Birth field for Personal Contacts is now a RichFaces date picker widget, and the Number of Staff field for Business Contacts is a RichFaces slider widget.

Going futher, Metawidget's pluggable layouts make it easy to support third-party layout components. Edit WEB-INF/metawidget.xml again:

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="1.0"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
	...
	<inspector>		
		<compositeInspector xmlns="java:org.metawidget.inspector.composite"
								config="CompositeInspectorConfig">
			<inspectors>
			<array>
				<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
				<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"/>
				<facesAnnotationInspector xmlns="java:org.metawidget.inspector.faces"/>			
				<xmlInspector xmlns="java:org.metawidget.inspector.xml" config="XmlInspectorConfig"/>
			</array>
			</inspectors>
		</compositeInspector>
	</inspector>
	<widgetBuilder>
		<compositeWidgetBuilder xmlns="java:org.metawidget.widgetbuilder.composite"
			config="CompositeWidgetBuilderConfig">
			<widgetBuilders>
				<array>
					<overriddenWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
					<readOnlyWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
					<richFacesWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder.richfaces"/>
					<htmlWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
				</array>
			</widgetBuilders>
		</compositeWidgetBuilder>
	</widgetBuilder>
	<layout>
		<tabPanelLayoutDecorator xmlns="java:org.metawidget.faces.component.html.layout.richfaces"
			config="TabPanelLayoutDecoratorConfig">
			<layout>
				<simpleLayout xmlns="java:org.metawidget.faces.component.layout"/>
			</layout>
		</tabPanelLayoutDecorator>
	</layout>	
</htmlMetawidget>

</metawidget>

Restart Tomcat, refresh your Web browser and click on Homer Simpson again. Notice how the Contact Details and Other sections are laid out as tabs within a RichFaces TabPanel as in Figure 1.27.

Web Address Book using JBoss RichFaces

Figure 1.27. Web Address Book using JBoss RichFaces


This demonstrates how easy it is to leverage widget libraries with Metawidget (this example cheats a bit, as we've pre-added the RichFaces JARs into WEB-INF/lib and some lines into web.xml, but you get the idea).

Alternate Widget Libraries (JSF 2.x)

This section is specific to JSF 2.x.

As in the previous section, we can override Metawidget's default and introduce an alternate WidgetBuilder alongside the standard JSF HtmlWidgetBuilder. This time we will demonstrate using PrimeFaces.

Go into Tomcat's webapps/addressbook-faces2 folder (the exploded WAR) and edit WEB-INF/metawidget.xml:

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="1.0"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
	...
	<inspector>		
		<compositeInspector xmlns="java:org.metawidget.inspector.composite"
								config="CompositeInspectorConfig">
			<inspectors>
			<array>
				<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
				<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"/>
				<facesAnnotationInspector xmlns="java:org.metawidget.inspector.faces"/>			
				<xmlInspector xmlns="java:org.metawidget.inspector.xml" config="XmlInspectorConfig"/>
			</array>
			</inspectors>
		</compositeInspector>
	</inspector>
	<widgetBuilder>
		<compositeWidgetBuilder xmlns="java:org.metawidget.widgetbuilder.composite"
			config="CompositeWidgetBuilderConfig">
			<widgetBuilders>
				<array>
					<overriddenWidgetBuilder xmlns="java:org.metawidget.faces.component.widgetbuilder"/>
					<readOnlyWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
					<primeFacesWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder.primefaces"/>
					<htmlWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
				</array>
			</widgetBuilders>
		</compositeWidgetBuilder>
	</widgetBuilder>
	<layout>
		<tabViewLayoutDecorator xmlns="java:org.metawidget.faces.component.html.layout.primefaces"
			config="org.metawidget.layout.decorator.LayoutDecoratorConfig">
			<layout>
				<simpleLayout xmlns="java:org.metawidget.faces.component.layout"/>
			</layout>
		</tabViewLayoutDecorator>
	</layout>	
</htmlMetawidget>

</metawidget>

Restart Tomcat, refresh your Web browser and click on Charles Montgomery Burns. Notice how the Contact Details and Other sections are laid out as tabs within a PrimeFaces TabView and the Number of Staff field is a PrimeFaces Slider. See Figure 1.28.

Web Address Book using PrimeFaces

Figure 1.28. Web Address Book using PrimeFaces


This demonstrates how easy it is to leverage widget libraries with Metawidget (this example cheats a bit, as we've pre-added the PrimeFaces JARs into WEB-INF/lib and some lines into web.xml, but you get the idea).

1.3.3 Mobile Address Book

For the Mobile Address Book we use the Android platform. Like Web-based applications, mobile applications require a container to run. If you have a physical Android device you can install the Mobile Address Book by downloading and opening http://metawidget.org/examples/android/addressbook-android.apk or scanning this QR Code:

Mobile Address Book APK URL

Figure 1.29. Mobile Address Book APK URL


[Tip]Note
The Android Address Book is a native mobile application. For an example of an HTML 5 hybrid mobile application see the JQuery Mobile Address Book in the previous section.

Alternatively download the Android SDK from http://code.google.com/android/download.html (the examples are tested against Android 1.1). Then change to the installation directory (usually defined by ANDROID_HOME) and run the emulator by opening a command prompt and typing:

tools\android create avd -n my_avd -t 1

Replacing -t 1 with the Android version you're using (e.g. -t 2 for 1.5, -t 3 for 1.6, -t 4 for 2.0). Android 1.1 doesn't use AVDs, so you can skip this step. Next type:

tools\emulator -avd my_avd

The emulator may take a little while to start. Once finished, it will display the phone's desktop. The Address Book APK is pre-built for you in examples/android/addressbook-android.apk. Alternatively you can build it yourself by changing to the src/examples folder and typing:

mvn -pl org.metawidget.examples.android:addressbook-android -am integration-test

Next, open a second command prompt, change to the Android installation directory and type:

platform-tools\adb install <metawidget folder>\examples\android\addressbook-android.apk

This deploys the APK into the emulator. To run it, click the emulator's Menu button and then choose the Address Book application. The emulator displays a search filter (at the top) and lists existing Address Book entries (at the bottom) as in Figure 1.30.

Mobile Address Book opening screen

Figure 1.30. Mobile Address Book opening screen


As with the Desktop and Web Address Books, the three search filter fields are created by Metawidget (this time AndroidMetawidget) based on the ContactSearch business class. Again, this includes populating the Type dropdown.

[Tip]Note
As with the Desktop Address Book, full source code for the examples can be found under src/examples. To open it in Eclipse we recommend installing Maven Integration for Android Development Tools.

The look of the screen relies entirely on Android XML layout files, styles and themes. Only the 'one column for the label, one column for the widget' layout is dictated by Metawidget, and that is pluggable and configurable.

Choose Add Personal from the Android menu. The page displays a form for filling out Personal Contact information as in Figure 1.31.

Mobile Address Book 'Add Personal Contact' screen

Figure 1.31. Mobile Address Book 'Add Personal Contact' screen


UIs in Android are typically defined using XML layout files, though they can also be built programmatically. AndroidMetawidget supports both approaches. For example, the Personal Contact screen is defined in contact.xml, and contains a Metawidget defined in much the same way as in JSP (including configuring section style and overriding widget creation):

<view class="org.metawidget.android.widget.AndroidMetawidget" android:id="@+id/metawidget"
	config="@raw/metawidget">
	
	<view class="org.metawidget.android.widget.Stub" tag="communications">
			
		<ListView android:id="@id+/communications" ... />
	 			
		<Button android:id="@+id/buttonAddCommunication"
			android:text="@string/addCommunication" ... />
	
	</view>
 
</view>

Within CommunicationDialog, a Metawidget is defined programatically in much the same way as in Swing:

mMetawidget = new AndroidMetawidget( activity );
mMetawidget.setConfig( R.raw.config );				
...
mMetawidget.setToInspect( mCommunication );

This produces the dialog box in Figure 1.32.

Mobile Address Book Communications Dialog

Figure 1.32. Mobile Address Book Communications Dialog


1.3.4 Conclusion

That concludes the introductory tutorial. In summary, we have now:

  • seen how to build an application whose UI is largely dictated by its business classes, not by hand-written UI code

  • significantly reduced the amount of UI code needed in our applications

  • seen how to build an application that targets multiple platforms. If we were to add a new property to one of the business classes (say, numberOfChildren to PersonalContact), it would automatically appear and be functional on every platform.

1.4 Part 3 - Other Examples

The Metawidget distribution includes other examples showcasing particular features on particular platforms. These additional examples are not a required part of the tutorial, but you may find them useful depending on which platform you use.

1.4.1 Swing Applet Address Book Example

The Swing Applet Address Book Example demonstrates using Metawidget in applets. The example is pre-built for you in examples/swing/applet/addressbook. Alternatively you can build it yourself by changing to the src/examples folder and typing:

mvn -pl org.metawidget.examples.swing:addressbook-swing-applet -am integration-test

To run the applet, open the index.html file in a Web browser. The code is identical to the Swing Address Book covered in Part 2 of this tutorial, except it uses org.metawidget.example.swing.applet.AddressBookApplet instead of org.metawidget.example.swing.addressbook.MainFrame.

The notable feature of the example is how the applet is packaged. Metawidget is highly modular and has no mandatory third-party JAR dependencies. The addressbook-swing-applet Maven POM uses fine-grained Metawidget dependencies (as an alternative to metawidge-all.jar) to include only those modules necessary for the applet. The resulting small download size makes Metawidget very viable for applet-based environments. See Section 9.5.1, “JAR Size”.

1.4.2 Seam Example

The Seam Booking Example demonstrates updating an existing Seam application to use Metawidget, reducing boilerplate code. The example is included as part of Seam 2.2.0.GA. It also requires you to have JBoss 5.1.0.GA, and you should be familiar with the existing Seam Booking application.

The example is located in jboss-seam-2.2.0.GA/examples/metawidget/booking. It is not pre-built. To build it, change to the examples/metawidget/booking folder in the Seam binary distribution and type:

ant

To run it, type:

cd \Applications\jboss-5.1.0.GA
bin\run

Open a Web browser to http://localhost:8080/seam-booking. The updated Metawidget Seam Booking Example looks very similar to the original, as in Figure 1.33, but uses significantly less boilerplate code.

Seam Booking with Metawidget

Figure 1.33. Seam Booking with Metawidget


The files modified for adding Metawidget support are in examples/metawidget/booking. Most of the UI code in view/book.xhtml, view/confirm.xhtml and view/hotelview.xhtml has been replaced with a single Metawidget tag. Some annotations have been added to Hotel.java and Booking.java, though Metawidget also leverages the existing JPA and Hibernate Validator ones.

1.4.3 Groovy Example

The Seam Groovy Booking Example demonstrates updating an existing Seam Groovy application to use Metawidget, reducing boilerplate code. The example is a more advanced version of the previous Seam section, so you should work through that first.

The example is located in jboss-seam-2.2.0.GA/examples/metawidget/groovybooking. It is not pre-built. To build it, change to the examples/metawidget/groovybooking folder in the Seam distribution and type:

ant

To run it, type:

cd \Applications\jboss-5.1.0.GA
bin\run

Open a Web browser to http://localhost:8080/jboss-seam-groovybooking. As with the previous section, the updated Metawidget Seam Groovy Booking Example looks very similar to the original, but uses significantly less boilerplate code. This time we are using Groovy to define our business classes. The biggest impact this has is in metawidget.xml, where the Inspectors have been configured to use a Groovy property style instead of a JavaBean property style.

Metawidget supports pluggable 'property styles' for JavaBean, Groovy and other property styles. Groovy properties differ from JavaBean properties in that their annotations are tied to the private field, rather than the getters and setters. The use of Groovy is configured per Inspector, as in examples/metawidget/groovybooking/resources/WEB-INF/metawidget.xml:

<propertyTypeInspector config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<propertyStyle>
		<groovyPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.groovy"/>
	</propertyStyle>
</propertyTypeInspector>

The example demonstrates how Metawidget can significantly reduce the amount of boilerplate code in your UI layer - in some cases up to 70%. Figure 1.33 shows the book.xhtml page before and after being fitted with Metawidget. The red boxes and lines highlight the chunks of boilerplate that have been removed:

book.xhtml before and after being fitted with Metawidget

Figure 1.34. book.xhtml before and after being fitted with Metawidget


1.4.4 jBPM Example

The Seam DVD Store Example demonstrates updating an existing Seam jBPM application to use Metawidget, reducing boilerplate code. The example requires you to have previously downloaded Seam 2.2.0.GA and JBoss 5.1.0.GA, and you should be familiar with the existing Seam DVD Store application.

The example is located in jboss-seam-2.2.0.GA/examples/metawidget/dvdstore. It is not pre-built. To build it, change to the examples/metawidget/dvdstore folder in the Seam distribution and type:

ant

To run it, type:

cd \Applications\jboss-5.1.0.GA
bin\run

Open a Web browser to http://localhost:8080/seam-dvdstore. As with the previous two sections, the updated Metawidget Seam DVD Store example looks very similar to the original, as in Figure 1.35, but uses significantly less boilerplate code.

Seam DVD Store with Metawidget

Figure 1.35. Seam DVD Store with Metawidget


This time, as well as generating UIComponents for domain objects such as com.jboss.dvd.seam.Customer, Metawidget inspects jBPM pageflow files like newuser.jpdl.xml and checkout.jpdl.xml to generate the correct UICommand buttons for each screen.

1.4.5 ICEfaces Example

ICEfaces is an AJAX component library for Java Server Faces. This example showcases how Metawidget can work with ICEfaces (1.8.2 and above) to deliver rich AJAX applications.

The example is pre-built for you in examples/faces/penguincolony-faces.war. Copy the WAR into Tomcat's webapps folder, start Tomcat, and open a Web browser to http://localhost:8080/penguincolony-faces:

ICEfaces with Metawidget

Figure 1.36. ICEfaces with Metawidget


The application manages details of penguins in a colony. To begin, click the Edit link in the first row of the table: ICEfaces and Metawidget work together to pop up an AJAX form without refreshing the page. Next, clear the form's Name box and tab to the next field: an AJAX call is made and a validation error appears. Try entering a new name and tabbing again: the validation error disappears and the new name is immediately reflected in the table behind the popup box.

The remaining fields are wrapped in an ICEfaces PanelTabSet. Click on the Details tab. Here, the example makes use of the @UiAttribute annotation and FacesInspectionResultProcessor. The Java code is annotated:

@UiAction
@UiAttribute( name = InspectionResultConstants.HIDDEN, value = "#{!empty _this.condition}" )
public void addCondition() { ... }

@UiAttribute( name = InspectionResultConstants.HIDDEN, value = "#{empty _this.condition}" )
public PenguinCondition getCondition() { ...}

Clicking the Add Condition button, or checking one of the Hobbies checkboxes, triggers an AJAX call that re-evaluates the @UiAttribute annotations and dynamically reconstructs the form without requiring a page refresh. This includes removing existing buttons, creating new dropdown boxes and creating new labels.

For more details on ICEfaces support, see the section called “IceFacesWidgetBuilder” and the section called “PanelTabSetLayoutDecorator”.

1.4.6 Java EE 6 Example

There is a retrofitted version of the GatewayWarDeployment example from Adam Bien's Real World Java EE Patterns book downloadable from his Java EE Patterns and Best Practices Kenai Repository.

For more information, see this blog entry.

1.4.7 Swing AppFramework Example

The Swing AppFramework Car Demo demonstrates using Metawidget with the Swing AppFramework. Metawidget can use Swing AppFramework's @Action annotation to identify actions, both amongst a domain object's properties and in external controllers, and automatically generate JButtons for them.

The application is pre-built for you in examples/swing/appframework-swing.jar or you can build it yourself by changing to the src/examples/swing/appframework folder and typing:

mvn package
[Tip]Note
This example uses annotations, so you'll need Java SE 5 or higher

This is a self-executing JAR. For convenience, it has MANIFEST.MF dependencies hard-wired into it to lib/AppFramework.jar among others, so it's best not to move it to a different folder (if you do, you'll need to manually put those JARs on your CLASSPATH).

Run the code by navigating to the examples/swing folder and typing:

java -jar appframework-swing.jar

The opening screen displays two fields to allow you to enter the make and type of a car. You can also optionally add an owner by clicking the Add an Owner button, or save the car using the Save button.

The Add an Owner button is generated by Metawidget based on the addOwner method in the Car class (which has been annotated @org.jdesktop.application.Action). The Save button is generated based on the save method in the CarApplication class (also annotated @Action). Two different Metawidgets are used in the example: one pointed at the Car class, the other at the CarApplication class.

Metawidget supports pluggable 'action styles'. The use of Swing AppFramework is configured per Inspector, as in src/examples/swing/appframework/src/main/resources/org/metawidget/example/swing/appframework/metawidget.xml from the source distribution:

<metawidgetAnnotationInspector config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<actionStyle>
		<swingAppFrameworkActionStyle xmlns="java:org.metawidget.inspector.impl.actionstyle.swing">
	</actionStyle>
</metawidgetAnnotationInspector>

As a further feature, after the Add an Owner button is clicked it disappears. This is acheived by using JexlInspectionResultProcessor to introduce an expression language for Swing similar to JSP's EL. The method is annotated...

@Action( name = "add" )
@UiAttribute( name = HIDDEN, value = "${this.owner != null}" )
public void addOwner() {
	mOwner = new Owner();
	fireActionEvent( "addOwner" );
}

...such that the button gets hidden when the car has an owner.

1.4.8 Scala Example

The Scala Animal Races Example demonstrates using SwingMetawidget together with Scala and MigLayout.

The application is pre-built for you in examples/swing/animalraces-swing.jar or you can build it yourself by changing to the src/examples/swing/animalraces folder of the source distribution and typing:

mvn package
[Tip]Note
This example uses annotations, so you'll need Java SE 5 or higher

This is a self-executing JAR. For convenience, it has MANIFEST.MF dependencies hard-wired into it to lib/scala-library.jar among others, so it's best not to move it to a different folder (if you do, you'll need to manually put those JARs on your CLASSPATH).

Run the code by navigating to the examples/swing folder and typing:

java -jar animalraces-swing.jar

The screen displays fields to allow you to change the name, speed and type of each animal as well buttons to start and stop the race.

Scala and MigLayout with Metawidget

Figure 1.37. Scala and MigLayout with Metawidget


Animal Races' whimsical User Interface demonstrates how Metawidget's goal of not 'owning' the UI allows multiple Metawidgets to be combined for unconventional UIs. There are three Metawidgets across the top (one for each animal in the race), and a fourth Metawidget for the buttons at the bottom.

The top three Metawidgets all use MigLayout. Because Metawidget does not hide the underlying UI framework, using MigLayout allows the Animal Races code to easily pad the Metawidget:

metawidget.setMetawidgetLayout( new MigLayout() );
metawidget.setToInspect( animal );
((MigLayout) metawidget.getLayout()).setLayoutConstraints( new LC().insets( "10" ));

The Animal Races code is written purely in Scala, located at src/examples/swing/animalraces/src/main/scala/org/metawidget/example/swing/animalraces/AnimalRaces.scala. It uses ScalaPropertyStyle to allow Metawidget to inspect Scala-based domain objects:

<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"
		config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
		<propertyStyle>
			<scalaPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.scala"/>
		</propertyStyle>			
	</metawidgetAnnotationInspector>

In addition, it uses BeanUtilsBindingProcessorConfig.PROPERTYSTYLE_SCALA to bind JComponents to Scala-based domain objects:

val metawidget = new SwingMetawidget()
metawidget.addWidgetProcessor( new BeanUtilsBindingProcessor()
			.setPropertyStyle( BeanUtilsBindingProcessorConfig.PROPERTYSTYLE_SCALA ))
metawidget.setToInspect( animal )

1.4.9 GWT Client Side Example

By default, GwtMetawidget uses server-side inspection of domain objects. This is because client-side JavaScript does not support reflections or annotations. However if you don't need reflections or annotations, and have your own way of retrieving inspection results, you can plug in your own Inspector and keep everything client-side. This example implements a TextAreaInspector that retrieves inspection results from a textarea and generates the UI.

The GWT Client Side example is pre-built for you in examples/gwt/clientside-gwt. Because everything is purely client side, there is no need for Tomcat or any other container: simply navigate to the example folder and open index.html in your Web browser:

Client-Side GwtMetawidget

Figure 1.38. Client-Side GwtMetawidget


The example shows a textarea on the left containing inspection results in inspection-result-1.0.xsd format. The result of generating this XML is shown on the right. Click the Sample #2 and Sample #3 buttons to preload different XML samples, and the Generate button to generate their UI. Alternatively, you can edit the XML by hand to add new properties and actions and click Generate.

Data binding and event binding are also implemented client side. Click the Save to save the data from the generated UI using a MapPropertyBinding (in a real application, this Map could be passed back to the server for persisting). Click the Add Tracks button to trigger an event binding.

Finally, this example showcases using third-party GWT component libraries. ExtGwtWidgetBuilder is used to render a date picker widget. For more details on ExtGWT support, see the section called “ExtGwtWidgetBuilder”.

1.4.10 GWT Hosted Mode Examples

The examples/gwt/addressbook-gwt.war (discussed in Part 2 of this tutorial) and the examples/gwt/clientside (discussed in Part 3 of this tutorial) demonstrates GWT running in GWT Web mode. Developers may prefer instead to run the examples in GWT hosted mode as in Figure 1.39.

Address Book Example running in GWT Hosted Mode

Figure 1.39. Address Book Example running in GWT Hosted Mode


Source code for all examples can be found under the src/examples folder of the examples distribution. The code is organized into standalone Maven projects so that it can be easily imported into your favourite IDE. For example, to run the GWT Address Book example in Eclipse:

  • Install m2eclipse, then choose File > Import... > Maven > Existing Maven Projects

  • Navigate to src/examples/gwt/addressbook and choose to import pom.xml

  • The project may take a while to import and download dependencies (shown under Eclipse's Console tab). Afterwards, there will be build errors related to missing artifacts (shown under Eclipse's Problems tab)

  • Right click the project and choose Run As > Maven Build... (note the ellipses). In the Edit Configuration dialog, remove /gwt/addressbook from the end of Base directory and enter Goals as:

    -pl org.metawidget.examples.gwt:addressbook-gwt -am install

    Click Run and wait for all artifacts to build. Again, this may take a while.

  • Choose Project > Update All Maven Dependencies. The build errors should disappear.

  • Install the Google Plugin for Eclipse, then right click the project and choose Properties > Google > Web Toolkit and click Use Google Web Toolkit.

  • There will be a build error related to a missing web.xml. Right click the project and choose Refresh. The build error should disappear.

  • Right click the project and choose Run As > Web Application.

  • Choose hosted.jsp (this is a simplified home page that doesn't invoke custom JSTL tags).

  • Open the Google Development Mode view tab and double-click to open the URL in your browser.

[Important]Check your Build Path
If Hosted Mode fails to start, right click the project and choose Properties > Java Build Path > Libraries. Check the build path does not include any GWT libraries outside of those in Maven Dependencies, and that the GWT version matches the one in src/examples/gwt/addressbook/pom.xml.

Once you have the samples running, you can quickly make changes and play around. For example:

  • Find the line in ContactDialog that configures a LabelLayoutDecorator (which decorates sections using Labels).

  • Change it to a TabPanelLayoutDecorator. This will decorate sections using a GWT TabPanel.

  • Right click the pom.xml and choose Run As > Maven Package (this step is only necessary because the GWT Address Book uses shared projects. Import the GWT Client Side project for an example configured to deploy immediately without needing this extra step).

  • Refresh your browser. The application will be using TabPanel, as shown in Figure 1.40.

Address Book Example using GWT TabPanel

Figure 1.40. Address Book Example using GWT TabPanel


1.4.11 Static Metawidget Example

Although Metawidget is focused on runtime UI generation, its static mode (see Section 3.4, “Static Metawidgets”) can sometimes be more appropriate. The JBoss Forge project provides a thorough, real world demonstration of static Metawidget in action. JBoss Forge uses Metawidget internally for its UI scaffolding generation.

Static Metawidget is used internally by JBoss Forge

Figure 1.41. Static Metawidget is used internally by JBoss Forge


To try using static Metawidget inside JBoss Forge:

  • Download Forge

  • Install it

  • Run it

  • Execute the following command:

    $ run-url https://raw.github.com/forge/core/master/showcase/posale.fsh

This runs a Forge script that automates the creation of a Java EE web application. Forge makes use of static Metawidget in several places, including: for the JSF components on the Create, Retrieve, Update and Delete (CRUD) screens; for the search filter criteria; for generating Java statements inside the JSF managed bean.

Alternatively, there are Forge plugins that can generate scaffolds for different environments. For example the Spring plugin uses StaticSpringMetawidget to generate Spring scaffolds, and the AeroGear plugin uses StaticHtmlMetawidget to generate mobile scaffolds. For more information on JBoss Forge and how Metawidget relates to it, see the UI scaffolding section of the Forge documentation.

2. Architecture

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.

Metawidget uses a five stage pipeline

Figure 2.1. Metawidget uses a five stage pipeline


The five stages of the pipeline are:

  1. 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.

  2. 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'.

  3. 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).

  4. 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.

  5. 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).

[Tip]Immutable
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.
[Tip]Research
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, see this PhD thesis.

The following sections discuss each of the five stages of the pipeline in detail.

2.1 Metawidgets

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.

2.1.1 Interface

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:

  1. 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).

  2. 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.

  3. 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.

  4. if WidgetBuilder.buildWidget returns null for the top-level element, calls buildCompoundWidget to iterate over each child of the top-level element.

  5. 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.

  6. the created widgets are passed through a series of WidgetProcessors. These can apply binding mechanisms, validators and so on.

  7. 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.

2.1.2 Customizing Look and Feel

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).

2.1.3 Overriding Widget Creation

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 domain 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.

2.1.4 Implementing Your Own Metawidget

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.

2.2 Inspectors

Inspectors inspect the back-end architecture looking for useful metadata. This section covers Inspectors in general. For in-depth documentation of individual Inspectors see Chapter 4, Inspectors.

2.2.1 Interface

All Inspectors must implement the Inspector interface. This is a simple interface that defines only one method:

String inspect( Object toInspect, String type, String... names );

Each Inspector must look to the type parameter and the names array. These form a path into the domain object model. For example the type may be com.myapp.Person and the names may be address and street. This would form a path into the domain model of com.myapp.Person/address/street (i.e. return information on the street property within the address property of the Person type).

Depending on the type of inspector, it may use the given toInspect to access the runtime object for the given type. For example:

metawidget.setToInspect( myPerson );	// Will be passed to Inspector

Or it may ignore the toInspect and look up information for the given type from an XML file or a database schema. This allows Metawidget to inspect types that have no corresponding Java object. For example:

metawidget.setToInspect( null );	// No setToInspect
metawidget.setPath( "Login Form" );

This could be combined with, say, an XmlInspector and a metawidget-metadata.xml:

<entity type="Login Form">
	<property name="username"/>
	<property name="password"/>
</entity>

This approach also allows Metawidget to inspect abstract classes:

metawidget.setToInspect( null );	// No setToInspect
metawidget.setPath( MyAbstractClass.class.getName() );
[Tip]Note
In general, a non-null setToInspect is preferrable, as many binding and validation technologies (e.g. see the section called “Property Binding”) will be expecting a concrete object.

2.2.2 Usage

Unless explicitly specified, each Metawidget will instantiate a default Inspector. Typically this will be a CompositeInspector composed of a PropertyTypeInspector and a MetawidgetAnnotationInspector.

This default behaviour can be overridden either in code:

metawidget.setInspector( new MyInspector() );

Or via metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<swingMetawidget xmlns="java:org.metawidget.swing">
	<inspector>
		<myInspector xmlns="java:com.myapp"/>
	</inspector>
</swingMetawidget>

This allows easy plugging in of alternate inspectors. Note that overriding the default means the default is no longer instantiated. In the example above, this would mean MyInspector is used but the default Inspectors are not. This is usually not what you want, because MyInspector will be focused on a particular type of back-end metadata and will want to leave other metadata to other inspectors.

To achieve this, use CompositeInspector.

2.2.3 CompositeInspector

CompositeInspector composes the results of several Inspectors into one and returns a single, combined inspection result. As shown in Figure 2.2 CompositeInspector works by calling each Inspector in turn, combining the inspection result as it goes.

CompositeInspector composes multiple inspectors into one

Figure 2.2. CompositeInspector composes multiple inspectors into one


All Inspectors are required to be immutable (see Section 2.2.5, “Immutability”). Therefore, although CompositeInspector maintains a list of Inspectors this must not be changeable. To enforce this, the list is set at instantation time using CompositeInspectorConfig. This can either be set in code:

metawidget.setInspector( new CompositeInspector( new CompositeInspectorConfig()
	.setInspectors(
		new PropertyTypeInspector(),
		new MetawidgetAnnotationInspector(),
		new MyInspector()
	)));

Or via metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<swingMetawidget xmlns="java:org.metawidget.swing">
<inspector>
	<compositeInspector xmlns="java:org.metawidget.inspector.composite" config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
				<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation" />
				<myInspector xmlns="java:com.myapp"/>
			</array>
		</inspectors>
	</compositeInspector>
</inspector>
</swingMetawidget>

2.2.4 Defaults

All Metawidgets have default Inspectors. 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 Inspector (i.e. use CompositeInspector). 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
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector />
				<metawidgetAnnotationInspector />
			</array>
		</inspectors>
	</compositeInspector>
GWT
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector />
				<metawidgetAnnotationInspector />
			</array>
		</inspectors>
	</compositeInspector>
JavaScript (incl. AngularJS and JQuery UI)
new metawidget.inspector.PropertyTypeInspector()
JSF
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector />
				<metawidgetAnnotationInspector />
				<facesAnnotationInspector />
			</array>
		</inspectors>
	</compositeInspector>
JSP
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector />
				<metawidgetAnnotationInspector />
				<jspAnnotationInspector />				
			</array>
		</inspectors>
	</compositeInspector>
Spring
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector />
				<metawidgetAnnotationInspector />
				<springAnnotationInspector />
			</array>
		</inspectors>
	</compositeInspector>
Struts
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector>
					<propertyStyle>
						<javaBeanPropertyStyle>
							<excludeBaseType>
								<pattern>^(java|javax|org\.apache\.struts)\..*$</pattern>
							</excludeBaseType>
						</javaBeanPropertyStyle>
					</propertyStyle>
				</propertyTypeInspector>				
				<metawidgetAnnotationInspector />
				<strutsAnnotationInspector />
				<commonsValidatorInspector />
			</array>
		</inspectors>
	</compositeInspector>
Swing
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector />
				<metawidgetAnnotationInspector />
			</array>
		</inspectors>
	</compositeInspector>
SWT
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<array>
				<propertyTypeInspector />
				<metawidgetAnnotationInspector />
			</array>
		</inspectors>
	</compositeInspector>

2.2.5 Immutability

All Inspectors are required to be immutable. This means you only need a single instance of an Inspector 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 Inspectors in Java code you should reuse instances.

Note that immutable only means Inspectors cannot be changed once instantiated - it does not mean they cannot be configured. Many Inspectors have corresponding xxxConfig classes that allow them to be configured prior to instantation in a type-safe way. For example, a JpaInspector can be configured in code:

metawidget.setInspector( new JpaInspector( new JpaInspectorConfig().setHideIds( false )));

Or in metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<jpaInspector xmlns="java:org.metawidget.inspector.jpa" config="JpaInspectorConfig">
	<hideIds>
		<boolean>false</boolean>
	</hideIds>
</jpaInspector>

2.2.6 inspection-result

The inspection-result XML format is the 'glue' that holds everything together: the Metawidgets request it, the Inspectors provide it, and the WidgetBuilders base their choice of widgets on it.

It is a very simple format. As an example:

<inspection-result xmlns="http://metawidget.org/inspection-result" version="1.0">
	<entity type="com.myapp.Person">
		<property name="name" required="true"/>
		<property name="age" minimum-value="0"/>
	</entity>
</inspection-result>

Only a handful of XML attributes are mandatory (see inspection-result-1.0.xsd). Most, such as required and minimum-value, are provided at the discretion of the Inspector and recognised at the discretion of the WidgetBuilders, WidgetProcessors and Layouts. This loose coupling allows Inspectors to evolve independently for new types of metadata, WidgetBuilders to evolve independently with new types of widgets, and so on.

For JavaScript-based Metawidgets, inspection results are returned as JSON Schema:

{
	properties: {
		name: {
			required: true
		},
		age: {
			minimum-value: 0
		}
	}
}

2.2.7 Implementing Your Own Inspector (Java)

Metawidget inspects a wide variety of back-end architectures. If your chosen back-end architecture is not supported 'out of the box', you may need to implement your own Inspector.

All Inspectors must implement the org.metawidget.inspector.Inspector interface:

public interface Inspector {
	String inspect( Object toInspect, String type, String... names );
}

The interface has only one method: inspect. Its parameters are:

  • an Object to inspect. This may be null, or can be ignored for Inspectors inspecting static metadata (such as XML files)

  • a type. This must match the given Object, or some attribute in the inspected config file

  • a list of names to be traversed beneath the type

The returned String must be an XML document conforming to inspection-result-1.0.xsd. To assist development, deploy your Inspector within ValidatingCompositeInspector to automatically validate the returned DOM during testing.

A number of convenience base classes are provided for different inspectors:

  • BaseObjectInspector assists in inspecting annotations and properties (including support for different property styles, such as JavaBean properties or Groovy properties). Here is an example of a custom Inspector to inspect tooltip metadata from a custom annotation. It extends the code from the tutorial (see Section 1.1, “Part 1 (Java version) - The First Metawidget Application”).

    package com.myapp;
    			
    import java.lang.annotation.*;
    import java.util.*;
    import javax.swing.JFrame;
    import org.metawidget.inspector.composite.*;
    import org.metawidget.inspector.impl.*;
    import org.metawidget.inspector.impl.propertystyle.*;
    import org.metawidget.inspector.propertytype.*;
    import org.metawidget.swing.*;
    import org.metawidget.util.*;
    
    public class Main {
    
    	public static void main( String[] args ) {
    		Person person = new Person();
    
    		SwingMetawidget metawidget = new SwingMetawidget();
    		CompositeInspectorConfig inspectorConfig = new CompositeInspectorConfig().setInspectors(							
    					new PropertyTypeInspector(),
    					new TooltipInspector() );
    		metawidget.setInspector( new CompositeInspector( inspectorConfig ) );
    		metawidget.setToInspect( person );
    
    		JFrame frame = new JFrame( "Metawidget Tutorial" );
    		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    		frame.getContentPane().add( metawidget );
    		frame.setSize( 400, 250 );
    		frame.setVisible( true );
    	}
    	
    	@Retention( RetentionPolicy.RUNTIME )
    	@Target( { ElementType.FIELD, ElementType.METHOD } )
    	static @interface Tooltip {
    		String value();
    	}
    
    	static class TooltipInspector
    		extends BaseObjectInspector {
    
    		protected Map<String, String> inspectProperty( Property property )
    			throws Exception {
    			
    			Map<String, String> attributes = CollectionUtils.newHashMap();
    
    			Tooltip tooltip = property.getAnnotation( Tooltip.class );
    
    			if ( tooltip != null )
    				attributes.put( "tooltip", tooltip.value() );
    
    			return attributes;
    		}
    	}
    }

    You could then annotate the Person class...

    package com.myapp;
    
    import com.myapp.Main.Tooltip;
    
    public class Person {
    	@Tooltip("Person's full name")
    	public String name;
    	@Tooltip("Age in years")
    	public int age;
    	@Tooltip("Whether person is retired")
    	public boolean retired;
    }

    ...and TooltipInspector would pick up the custom @Tooltip annotation and feed it into the Metawidget pipeline.

    [Tip]Note
    Because Metawidget decouples inspection from widget creation, by default SwingMetawidget will not be expecting this new tooltip attribute and will ignore it. You will need to further combine this example with a TooltipProcessor, see Section 2.5.6, “Implementing Your Own WidgetProcessor”.
  • For inspecting XML files, BaseXmlInspector assists in opening and traversing through the XML, as well as merging multiple XML files into one (e.g. merging multiple Hibernate mapping files). Here is an example of a custom Inspector to inspect Castor XML mapping files:

    package com.myapp;
    
    import static org.metawidget.inspector.InspectionResultConstants.*;
    
    import java.util.*;
    import org.metawidget.inspector.impl.*;
    import org.metawidget.util.*;
    import org.w3c.dom.*;
    
    public class CastorInspector
    	extends BaseXmlInspector {
    	
    	public CastorInspector( BaseXmlInspectorConfig config ) {
    		super( config );
    	}
    
    	protected Map<String, String> inspectProperty( Element toInspect ) {
    	
    		if ( !"field".equals( toInspect.getNodeName() ) )
    			return null;
    
    		Map<String, String> attributes = CollectionUtils.newHashMap();
    		attributes.put( NAME, toInspect.getAttribute( getNameAttribute() ) );
    		attributes.put( TYPE, toInspect.getAttribute( getTypeAttribute() ) );
    		return attributes;
    	}
    
    	protected String getTopLevelTypeAttribute() {
    		return NAME;
    	}
    }

When implementing your own Inspector, try to avoid technology-specific XML attribute names. For example, FacesAnnotationInspector has an annotation @UiFacesNumberConverter. This annotation certainly has a technology-specific part to it, as it names a JSF Converter that only applies in JSF environments, so it is reasonable to name that XML attribute faces-converter-class. However, it also describes other parts of the property, such as the maximum number of integer digits. Such parts are not JSF-specific (i.e. we can source the same property from Hibernate Validator's @Digits annotation), so are better named 'neutrally' (i.e. maximum-integer-digits).

Like InspectionResultProcessors, WidgetBuilders, WidgetProcessors and Layouts, Inspectors are required to be immutable. However they will occasionally need to store some internal state, such as which PropertyStyle to use. This can be achieved in two ways:

  1. For state that will remain constant throughout the life of the Inspector, such as which PropertyStyle to use, use xxxInspectorConfig classes. For example:

    public class MyInspectorConfig {
    	private PropertyStyle mPropertyStyle;
    	
    	public MyInspectorConfig setPropertyStyle( String propertyStyle ) {
    		mPropertyStyle = propertyStyle;
    		return this;
    	}
    
    	public String getPropertyStyle() {
    		return mPropertyStyle;
    	}
    	
    	// ...must override equals and hashCode too...
    }

    These xxxInspectorConfig classes are then passed to the Inspector at construction time, and stored internally:

    public class MyInspector {
    	private PropertyStyle mPropertyStyle;
    	
    	public MyInspector( MyInspectorConfig config ) {
    		mPropertyStyle = config.getPropertyStyle();
    	}
    }

    This mechanism can then be controlled either programmatically:

    metawidget.setInspector( new MyInspector(
    	new MyInspectorConfig().setPropertyStyle( new JavaBeanPropertyStyle() )));

    Or in metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

    <myInspector xmlns="java:com.foo" config="MyInspectorConfig">
    	<propertyStyle>
    		<javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean"/>
    	</propertyStyle>
    </myInspector>
    [Important]Config classes must override equals and hashCode
    If you want your configurable Inspector to be cacheable and reusable by ConfigReader and metawidget.xml, the xxxInspectorConfig class must override equals and hashCode.
    [Tip]Generate an XML Schema
    If you intend your Inspector 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 Inspector 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.
  2. For state that will change across multiple inspections, store it in a ThreadLocal. This is straightforward because Inspectors are not re-entrant.

2.3 Inspection Result Processors

InspectionResultProcessors allow arbitrary processing of the inspection result returned by the Inspector, before it is passed to the WidgetBuilder. This section covers InspectionResultProcessors in general. For in-depth documentation of individual InspectionResultProcessors see Chapter 5, InspectionResultProcessors.

2.3.1 Interface

All InspectionResultProcessors must implement the InspectionResultProcessor interface. This is a simple interface that defines only one method:

String processInspectionResult( String inspectionResult, M metawidget, Object toInspect,
                                String type, String... names );

Where M is a Metawidget type (such as SwingMetawidget or UIMetawidget).

The InspectionResultProcessor must returned the processed inspection result as XML conforming to inspection-result-1.0.xsd. The parent Metwidget then passes this to the next InspectionResultProcessor in the list as shown in Figure 2.3.

Typical InspectionResultProcessor list

Figure 2.3. Typical InspectionResultProcessor list


In most cases the InspectionResultProcessor will modify the given inspectionResult and return a new String. This will then be passed down the list. Alternatively, the InspectionResultProcessor can return null to cancel inspection result processing entirely. No further InspectionResultProcessors or WidgetBuilders will be called, as shown in Figure 2.4.

An InspectionResultProcessor can abort the inspection result processing

Figure 2.4. An InspectionResultProcessor can abort the inspection result processing


2.3.2 Usage

Unless explicitly specified, each Metawidget will instantiate a default InspectionResultProcessor. Typically this will be a ComesAfterInspectionResultProcessor, which sorts business properties and actions according to their comes-after attribute.

This default behaviour can be overridden either in code:

metawidget.addInspectionResultProcessor( new MyInspectionResultProcessor() );

Or via metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<swingMetawidget xmlns="java:org.metawidget.swing">
	<inspectionResultProcessors>
		<array>
			<myInspectionResultProcessor xmlns="java:com.myapp"/>
		</array>
	</inspectionResultProcessors>
</swingMetawidget>

This allows easy plugging in of alternate InspectionResultProcessors.

2.3.3 Defaults

Most Metawidgets have default InspectionResultProcessors. Overriding the default means the default is no longer instantiated. This may not be what you want, so you may want to instantiate the default along with your new InspectionResultProcessor. 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
<array>
	<comesAfterInspectionResultProcessor />
</array>
AngularJS
new metawidget.angular.inspectionresultprocessor.AngularInspectionResultProcessor(...)
GWT
<array>
	<comesAfterInspectionResultProcessor />
</array>
JavaScript (incl. JQuery UI) (none)
JSF
<array>
	<facesInspectionResultProcessor />
	<comesAfterInspectionResultProcessor />
</array>
JSP
<array>
	<jspInspectionResultProcessor />
	<comesAfterInspectionResultProcessor />
</array>
Spring
<array>
	<comesAfterInspectionResultProcessor />
</array>
Struts
<array>
	<comesAfterInspectionResultProcessor />
</array>
Swing
<array>
	<comesAfterInspectionResultProcessor />
</array>
SWT
<array>
	<comesAfterInspectionResultProcessor />
</array>

2.3.4 Immutability

All InspectionResultProcessors are required to be immutable. This means you only need a single instance of an InspectionResultProcessor for your entire application. If you are using metawidget.xml then ConfigReader takes care of this for you, but if you are instantiating InspectionResultProcessors in Java code you should reuse instances.

2.3.5 Implementing Your Own InspectionResultProcessor

Here is an example of a custom InspectionResultProcessor that chooses, and sorts, domain object properties based on a JComponent client property. It extends the code from the tutorial (see Section 1.1, “Part 1 (Java version) - The First Metawidget Application”).

package com.myapp;
			
import static org.metawidget.inspector.InspectionResultConstants.*;

import javax.swing.*;
import org.metawidget.swing.*;
import org.metawidget.inspectionresultprocessor.iface.*;
import org.metawidget.util.*;
import org.w3c.dom.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.addInspectionResultProcessor( new IncludingInspectionResultProcessor() );
		metawidget.putClientProperty( "include", new String[]{ "retired", "age" } );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
	
	static class IncludingInspectionResultProcessor
		implements InspectionResultProcessor<SwingMetawidget> {
		
		public String processInspectionResult( String inspectionResult, SwingMetawidget metawidget,
		                                       Object toInspect, String type, String... names ) {
		
			String[] includes = (String[]) metawidget.getClientProperty( "include" );
			Document document = XmlUtils.documentFromString( inspectionResult );
			Element entity = (Element) document.getDocumentElement().getFirstChild();			
			int propertiesToCleanup = entity.getChildNodes().getLength();

			// Pull out the names in order

			for( String include : includes ) {
			
				Element property = XmlUtils.getChildWithAttributeValue( entity, NAME, include );

				if ( property == null )
					continue;

				entity.appendChild( property );
				propertiesToCleanup--;
			}

			// Remove the rest

			for( int loop = 0; loop < propertiesToCleanup; loop++ ) {
				entity.removeChild( entity.getFirstChild() );
			}

			return XmlUtils.documentToString( document, false );
		}
	}
}
[Tip]Note
We don't necessarily recommend this approach, as it requires hard-coding business property names into your UI screens and won't refactor well. See Chapter 9, How To's for other approaches.

Like Inspectors, WidgetBuilders, WidgetProcessors and Layouts, InspectionResultProcessors are required to be immutable. However they will occasionally need to store some internal state, such as which sort order to use. This can be achieved in two ways:

  1. For state that will remain constant throughout the life of the InspectionResultProcessor, such as which sort order to use, use xxxInspectionResultProcessorConfig classes. For example:

    public class MyInspectionResultProcessorConfig {
    	private boolean mSortAscending = true;
    	
    	public MyInspectionResultProcessorConfig setSortAscending( boolean sortAscending ) {
    		mSortAscending = sortAscending;
    		return this;
    	}
    
    	public boolean isSortAscending() {
    		return mSortAscending;
    	}
    	
    	// ...must override equals and hashCode too...
    }

    These xxxInspectionResultProcessorConfig classes are then passed to the InspectionResultProcessor at construction time, and stored internally:

    public class MyInspectionResultProcessor {
    	private boolean mSortAscending;
    	
    	public MyInspectionResultProcessor( MyInspectionResultProcessorConfig config ) {
    		mSortAscending = config.isSortAscending();
    	}
    }

    This mechanism can then be controlled either programmatically:

    metawidget.addInspectionResultProcessor( new MyInspectionResultProcessor(
    	new MyInspectionResultProcessorConfig().setSortAscending( false )));

    Or in metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

    <myInspectionResultProcessor xmlns="java:com.foo" config="MyInspectionResultProcessorConfig">
    	<sortAscending>
    		<boolean>false</boolean>
    	</sortAscending>
    </myInspectionResultProcessor>
    [Important]Config classes must override equals and hashCode
    If you want your configurable InspectionResultProcessor to be cacheable and reusable by ConfigReader and metawidget.xml, the xxxInspectionResultProcessorConfig class must override equals and hashCode.
    [Tip]Generate an XML Schema
    If you intend your InspectionResultProcessor 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 InspectionResultProcessor 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.
  2. For state that will change across multiple inspection result processings, store it in the Metawidget that is passed to processInspectionResult. You may want to further wrap the state in a small helper class. For example:

    public String processInspectionResult( String inspectionResult, SwingMetawidget metawidget,
    		                                       Object toInspect, String type, String... names ) {
    
    
    	...process inspection result...
    	getState( metawidget ).previousType = type;
    }
    
    private State getState( SwingMetawidget metawidget ) {
    	State state = (State) metawidget.getClientProperty( getClass() );
    
    	if ( state == null ) {
    		state = new State();
    		metawidget.putClientProperty( getClass(), state );
    	}
    
    	return state;
    }
    
    static class State {
    	String previousType;
    	...other state variables...
    }

    You could also consider storing the state in a ThreadLocal. This is straightforward because InspectionResultProcessors are not re-entrant.

2.4 WidgetBuilders

WidgetBuilders build widgets based on inspection results. This section covers WidgetBuilders in general. For in-depth documentation of individual WidgetBuilders see Chapter 6, WidgetBuilders.

2.4.1 Interface

All WidgetBuilders must implement the WidgetBuilder interface. This is a simple interface that defines only one method:

W buildWidget( String elementName, Map<String, String> attributes, M metawidget )

Where W is a widget type (such as JComponent or UIComponent) and M is a Metawidget type (such as SwingMetawidget or UIMetawidget).

Each WidgetBuilder must look to the elementName (which is typically just 'property' or 'action' from the inspection-result) and to the various attributes (as parsed from the inspection-result) and instantiate an appropriate widget. WidgetBuilders can use the given metawidget to help them if needed (e.g. to access a UI context with which to instantiate widgets). Typically the WidgetBuilders do not need to configure the widget beyond simply instantiating it: the job of setting ids, attaching validators, configuring bindings and so forth is done by the WidgetProcessors (see Section 2.5, “WidgetProcessors”).

2.4.2 Usage

Unless explicitly specified, each Metawidget will instantiate default WidgetBuilders to match the target platform. For example, SwingMetawidget will by default instantiate an OverriddenWidgetBuilder, WidgetBuilder and SwingWidgetBuilder.

This default behaviour can be overridden either in code:

metawidget.setWidgetBuilder( new MyWidgetBuilder() );

Or via metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<swingMetawidget xmlns="java:org.metawidget.swing">
	<widgetBuilder>
		<myWidgetBuilder xmlns="java:com.myapp"/>
	</widgetBuilder>
</swingMetawidget>

This allows easy plugging in of third-party widget libraries. Note that overriding the default means the default is no longer instantiated. In the example above, this would mean MyWidgetBuilder is used but OverriddenWidgetBuilder, ReadOnlyWidgetBuilder and SwingWidgetBuilder are not. This is usually not what you want, because MyWidgetBuilder will be focused on a particular third party library and will want to leave widget overriding to OverriddenWidgetBuilder, read-only widgets (i.e. labels) to ReadOnlyWidgetBuilder and standard widgets to SwingWidgetBuilder.

To achieve this, use CompositeWidgetBuilder.

2.4.3 Advanced Interface

The WidgetBuilder interface only has a single method. This allows it to take advantage of future Java language features such as:

final Object someObject = ...;

metawidget.setWidgetBuilder(
	#(String name, Map<String, String> attr, SwingMetawidget m)
	{ ... } );

However for those needing more control over the WidgetBuilder lifecycle there is an extended interface AdvancedWidgetBuilder. This interface defines two additional methods:

void onStartBuild( M metawidget );
				
void onEndBuild( M metawidget );

The first method, onStartBuild, is called at the start of the widget building process. WidgetBuilders may wish to act on this event to initialize themselves ready for processing. However it is acceptable to do nothing.

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. WidgetBuilders may wish to act on this event to clean themselves up following processing. However it is acceptable to do nothing.

2.4.4 CompositeWidgetBuilder

CompositeWidgetBuilder combines the widget libraries of several WidgetBuilders. It defers widget building to an internal list of WidgetBuilders, in order, and goes with the first one that returns non-null (see figure Figure 2.5).

CompositeWidgetBuilder composes multiple WidgetBuilders into one

Figure 2.5. CompositeWidgetBuilder composes multiple WidgetBuilders into one


In this way WidgetBuilders for third party UI component libraries (that provide specialized components) can be listed first, and WidgetBuilders for the platform's standard components can be listed last (as a 'fallback' choice).

CompositeWidgetBuilder can be instantiated either in code:

metawidget.setWidgetBuilder( new CompositeWidetBuilder( new CompositeWidgetBuilderConfig()
	.setWidgetBuilders(
		new OverriddenWidgetBuilder(), new ReadOnlyWidgetBuilder(),
		new MyWidgetBuilder(), new SwingWidgetBuilder()
	)));

Or via metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<swingMetawidget xmlns="java:org.metawidget.swing">
	<widgetBuilder>
		<compositeWidgetBuilder
			xmlns="java:org.metawidget.widgetbuilder.composite"
			config="CompositeWidgetBuilderConfig">
			<widgetBuilders>
				<array>
					<overriddenWidgetBuilder xmlns="java:org.metawidget.swing.widgetbuilder"/>
					<readOnlyWidgetBuilder xmlns="java:org.metawidget.swing.widgetbuilder"/>
					<myWidgetBuilder xmlns="java:com.myapp"/>
					<swingWidgetBuilder xmlns="java:org.metawidget.swing.widgetbuilder"/>
				</array>
			</widgetBuilders>
		</compositeWidgetBuilder>
	</widgetBuilder>
</swingMetawidget>

2.4.5 OverriddenWidgetBuilder

The first WidgetBuilder in the CompositeWidgetBuilder chain should generally be an OverriddenWidgetBuilder. This looks for existing child widgets that override default generation. What consitutes an 'overridden widget' varies from platform to platform. For example, for Swing any child widget with the same name will be taken as the override (see Section 1.1.8, “Controlling Widget Creation”). Android uses the tag attribute, JSF uses the value binding, and so on. For details on the OverriddenWidgetBuilder for your platform see Chapter 6, WidgetBuilders.

You can also choose to plug-in your own WidgetBuilder that detects 'overridden widgets' based on your own criteria. Here is an example of a custom WidgetBuilder that excludes widgets based on a JComponent client property. It extends the code from the tutorial (see Section 1.1, “Part 1 (Java version) - 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.swing.widgetbuilder.*;
import org.metawidget.widgetbuilder.composite.*;
import org.metawidget.widgetbuilder.iface.*;
import org.metawidget.util.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setWidgetBuilder( new CompositeWidgetBuilder<JComponent, SwingMetawidget>(
			new CompositeWidgetBuilderConfig<JComponent, SwingMetawidget>().setWidgetBuilders(
				new ExcludingWidgetBuilder(),
				new SwingWidgetBuilder() ) ) );
		metawidget.putClientProperty( "exclude", new String[]{ "age", "retired" } );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
	
	static class ExcludingWidgetBuilder
		implements WidgetBuilder<JComponent, SwingMetawidget> {
		public JComponent buildWidget( String elementName, Map<String, String> attributes,
										SwingMetawidget metawidget ) {
			String[] exclude = (String[]) metawidget.getClientProperty( "exclude" );

			if ( ArrayUtils.contains( exclude, attributes.get( NAME )))
				return new Stub();

			return null;
		}
	}
}
[Tip]Note
We don't necessarily recommend this approach, as it requires hard-coding business property names into your UI screens and won't refactor well.
[Tip]Note
Although you could adapt this approach to only include (instead of exclude) certain properties, you could not adapt it to include properties in the order specified in the client property. This is because WidgetBuilders only operate on single widgets at a time. Instead, see Section 2.3, “Inspection Result Processors”

2.4.6 ReadOnlyWidgetBuilder

The second WidgetBuilder in the CompositeWidgetBuilder chain should generally be a ReadOnlyWidgetBuilder. This builds standard platform widgets for properties with read-only="true" or no-setter="true" (i.e. labels).

The exception to this rule would be if you wanted to add a custom WidgetBuilder for a widget library that had its own read-only components, or if you wanted to customise the read-only handling. Here is an example of a custom WidgetBuilder that returns non-editable JTextFields (instead of JLabels) for read-only properties. It extends the code from the tutorial (see Section 1.1, “Part 1 (Java version) - 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.swing.widgetbuilder.*;
import org.metawidget.widgetbuilder.composite.*;
import org.metawidget.widgetbuilder.iface.*;
import org.metawidget.util.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setWidgetBuilder( new CompositeWidgetBuilder<JComponent, SwingMetawidget>(
			new CompositeWidgetBuilderConfig<JComponent, SwingMetawidget>().setWidgetBuilders(
				new ReadOnlyTextFieldWidgetBuilder(),
				new SwingWidgetBuilder() ) ) );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
	
	static class ReadOnlyTextFieldWidgetBuilder
		implements WidgetBuilder<JComponent, SwingMetawidget> {
		public JComponent buildWidget( String elementName, Map<String, String> attributes,
										SwingMetawidget metawidget ) {
			if ( !WidgetBuilderUtils.isReadOnly( attributes ) )
				return null;

			if ( TRUE.equals( attributes.get( HIDDEN )))
				return null;

			Class<?> clazz = ClassUtils.niceForName( attributes.get( TYPE ) );

			if ( String.class.equals( clazz ) || clazz.isPrimitive() ) {
				JTextField textField = new JTextField();
				textField.setEditable( false );

				return textField;
			}

			return null;
		}
	}
}

2.4.7 Defaults

All Metawidgets have default WidgetBuilders. 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 WidgetBuilder (i.e. use CompositeWidgetBuilder). 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
<compositeWidgetBuilder config="CompositeWidgetBuilderConfig">
	<widgetBuilders>
		<array>
			<overriddenWidgetBuilder />
			<readOnlyWidgetBuilder />
			<androidWidgetBuilder />
		</array>
	</widgetBuilders>
</compositeWidgetBuilder>
GWT CompositeWidgetBuilder containg an OverriddenWidgetBuilder, ReadOnlyWidgetBuilder and GwtWidgetBuilder
JavaScript (incl. AngularJS)
new metawidget.widgetbuilder.CompositeWidgetBuilder( [
	new metawidget.widgetbuilder.OverriddenWidgetBuilder(),
	new metawidget.widgetbuilder.ReadOnlyWidgetBuilder(),
	new metawidget.widgetbuilder.HtmlWidgetBuilder()
] )
JQuery UI
new metawidget.widgetbuilder.CompositeWidgetBuilder( [
	new metawidget.widgetbuilder.OverriddenWidgetBuilder(),
	new metawidget.jqueryui.widgetbuilder.JQueryUIWidgetBuilder(),	
	new metawidget.widgetbuilder.ReadOnlyWidgetBuilder(),
	new metawidget.widgetbuilder.HtmlWidgetBuilder()
] )
JSF
<compositeWidgetBuilder config="CompositeWidgetBuilderConfig">
	<widgetBuilders>
		<array>
			<overriddenWidgetBuilder />
			<readOnlyWidgetBuilder />
			<htmlWidgetBuilder />
		</array>
	</widgetBuilders>
</compositeWidgetBuilder>
JSP
<compositeWidgetBuilder config="CompositeWidgetBuilderConfig">
	<widgetBuilders>
		<array>
			<overriddenWidgetBuilder />
			<readOnlyWidgetBuilder />
			<htmlWidgetBuilder />
		</array>
	</widgetBuilders>
</compositeWidgetBuilder>
Spring
<compositeWidgetBuilder config="CompositeWidgetBuilderConfig">
	<widgetBuilders>
		<array>
			<overriddenWidgetBuilder />
			<readOnlyWidgetBuilder />
			<springWidgetBuilder />
		</array>
	</widgetBuilders>
</compositeWidgetBuilder>
Struts
<compositeWidgetBuilder config="CompositeWidgetBuilderConfig">
	<widgetBuilders>
		<array>
			<overriddenWidgetBuilder />
			<readOnlyWidgetBuilder />
			<strutsWidgetBuilder />
		</array>
	</widgetBuilders>
</compositeWidgetBuilder>
Swing
<compositeWidgetBuilder config="CompositeWidgetBuilderConfig">
	<widgetBuilders>
		<array>
			<overriddenWidgetBuilder />
			<readOnlyWidgetBuilder />
			<swingWidgetBuilder />
		</array>
	</widgetBuilders>
</compositeWidgetBuilder>
SWT
<compositeWidgetBuilder config="CompositeWidgetBuilderConfig">
	<widgetBuilders>
		<array>
			<overriddenWidgetBuilder />
			<readOnlyWidgetBuilder />
			<swtWidgetBuilder />
		</array>
	</widgetBuilders>
</compositeWidgetBuilder>
Vaadin
<compositeWidgetBuilder config="CompositeWidgetBuilderConfig">
	<widgetBuilders>
		<array>
			<overriddenWidgetBuilder />
			<readOnlyWidgetBuilder />
			<vaadinWidgetBuilder />
		</array>
	</widgetBuilders>
</compositeWidgetBuilder>

2.4.8 Immutability

All WidgetBuilders are required to be immutable. This means you only need a single instance of a WidgetBuilder for your entire application. If you are using metawidget.xml then ConfigReader takes care of this for you, but if you are instantiating WidgetBuilders in Java code you should reuse instances.

Note that immutable only means WidgetBuilders cannot be changed once instantiated - it does not mean they cannot be configured. Many WidgetBuilders have corresponding xxxConfig classes that allow them to be configured prior to instantation in a type-safe way. For example, a HtmlWidgetBuilder can be configured in code:

metawidget.setWidgetBuilder( new HtmlWidgetBuilder( new HtmlWidgetBuilderConfig()
			.setDataTableStyleClass( "data-table" )));

Or in metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<htmlWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"
	config="HtmlWidgetBuilderConfig">
	<dataTableStyleClass>
		<string>data-table</string>
	</dataTableStyleClass>
</htmlWidgetBuilder>

2.4.9 Implementing Your Own WidgetBuilder

The pluggable nature of WidgetBuilders makes it easy to add your own. Because CompositeWidgetBuilder can be used to chain WidgetBuilders together, you only need worry about supporting your particular widgets. You can simply return null for all other types of property and rely on another WidgetBuilder further down the chain to instantiate one of the usual widgets. For example, RichFacesWidgetBuilder only instantiates the RichFaces components, and returns null for everything else.

Here is an example of a custom WidgetBuilder that uses two JRadioButtons, instead of the usual JCheckBox, to represent boolean properties. It extends the code from the tutorial (see Section 1.1, “Part 1 (Java version) - The First Metawidget Application”).

package com.myapp;
			
import static org.metawidget.inspector.InspectionResultConstants.*;

import java.awt.*;
import java.util.*;
import javax.swing.*;
import org.metawidget.swing.*;
import org.metawidget.swing.widgetbuilder.*;
import org.metawidget.widgetbuilder.composite.*;
import org.metawidget.widgetbuilder.iface.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setWidgetBuilder( new CompositeWidgetBuilder<JComponent, SwingMetawidget>(
			new CompositeWidgetBuilderConfig<JComponent, SwingMetawidget>().setWidgetBuilders(
				new JRadioButtonWidgetBuilder(),
				new SwingWidgetBuilder() ) ) );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
	
	static class JRadioButtonWidgetBuilder
		implements WidgetBuilder<JComponent, SwingMetawidget> {

		public JComponent buildWidget( String elementName, Map<String, String> attributes,
			SwingMetawidget metawidget ) {
			
			if ( !"boolean".equals( attributes.get( TYPE ) ) )
				return null;

			JRadioButton trueButton = new JRadioButton( "True" );
			JRadioButton falseButton = new JRadioButton( "False" );
			JPanel panel = new JPanel();
			panel.setLayout( new GridLayout( 2, 1 ) );
			panel.add( trueButton );
			panel.add( falseButton );

			ButtonGroup buttonGroup = new ButtonGroup();
			buttonGroup.add( trueButton );
			buttonGroup.add( falseButton );

			return panel;
		}
	}
}

Like Inspectors, InspectionResultProcessors, WidgetProcessors and Layouts, WidgetBuilders are required to be immutable. However they will occasionally need to store some internal state, such as which CSS styls to use. This can be achieved in two ways:

  1. For state that will remain constant throughout the life of the WidgetBuilder, such as which CSS style to use, use xxxInspectorConfig classes. For example:

    public class MyWidgetBuilderConfig {
    	private String mCssStyle;
    	
    	public MyWidgetBuilderConfig setCssStyle( String cssStyle ) {
    		mCssStyle = cssStyle;
    		return this;
    	}
    
    	public String getCssStyle() {
    		return mCssStyle;
    	}
    	
    	// ...must override equals and hashCode too...
    }

    These xxxWidgetBuilderConfig classes are then passed to the WidgetBuilder at construction time, and stored internally:

    public class MyWidgetBuilder {
    	private String mCssStyle;
    	
    	public MyWidgetBuilder( MyWidgetBuilderConfig config ) {
    		mCssStyle = config.getCssStyle();
    	}
    }

    This mechanism can then be controlled either programmatically:

    metawidget.setWidgetBuilder( new MyWidgetBuilder(
    	new MyWidgetBuilderConfig().setCssStyle( "widget" )));

    Or in metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

    <myWidgetBuilder xmlns="java:com.foo" config="MyWidgetBuilderConfig">
    	<cssStyle>
    		<string>widget</string>
    	</cssStyle>
    </myWidgetBuilder>
    [Important]Config classes must override equals and hashCode
    If you want your configurable WidgetBuilder to be cacheable and reusable by ConfigReader and metawidget.xml, the xxxWidgetBuilderConfig class must override equals and hashCode.
    [Tip]Generate an XML Schema
    If you intend your WidgetBuilder 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 WidgetBuilder 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.
  2. For state that will change across multiple widgets, such as the name of the previous widget, store it in the Metawidget that is passed to buildWidget. You may want to further wrap the state in a small helper class. For example:

    public JComponent buildWidget( String elementName, Map<String, String> attributes,
    			SwingMetawidget metawidget ) {
    
    	...build new widget...
    	getState( metawidget ).previousName = attributes.get( NAME );
    }
    
    private State getState( SwingMetawidget metawidget ) {
    	State state = (State) metawidget.getClientProperty( getClass() );
    
    	if ( state == null ) {
    		state = new State();
    		metawidget.putClientProperty( getClass(), state );
    	}
    
    	return state;
    }
    
    static class State {
    	String previousName;
    	...other state variables...
    }

    You could also consider storing the state in a ThreadLocal. This is straightforward because WidgetBuilders are not re-entrant.

[Important]Config classes must override equals and hashCode
If you want your configurable WidgetBuilder to be cacheable and reusable by ConfigReader and metawidget.xml, the xxxWidgetBuilderConfig class must override equals and hashCode.
[Tip]Generate an XML Schema
If you intend your WidgetBuilder 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 WidgetBuilder 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.

Special considerations for Java Server Faces

When developing WidgetBuilders for JSF component libraries, be aware that Metawidget integrates with the JSF lifecycle in a slightly unorthodox way. Upon POSTback, Metawidget first decodes, processes validators and updates the business model as usual. Upon encodeBegin, however, Metawidget destroys and recreates all previously generated UIComponents. This is so the UIComponents can adapt to changes in the business model. For example, they might need to be changed from a UIOutputText to a UIInputText if the user clicks an Edit button.

In most cases such recreation works well, but on occasion a component may not function properly because it is not expecting to be recreated. For example, the ICEfaces SelectInputDate component stores the state of its date popup inside the UIComponent. If it is recreated, this state is lost and the popup never appears. For such components, WidgetBuilder authors can set the attribute UIMetawidget.COMPONENT_ATTRIBUTE_NOT_RECREATABLE (must be used in conjunction with OverriddenWidgetBuilder) on the UIComponent to prevent its destruction and recreation. Of course, this somewhat impacts its flexibility. For example, a SelectInputDate would not be able to change its date format in response to another component on the form.

2.5 WidgetProcessors

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 7, WidgetProcessors.

2.5.1 Interface

All WidgetProcessors must implement the WidgetProcessor interface. This is a simple interface that defines only one method:

W processWidget( W widget, String elementName, Map<String, String> attributes, M metawidget );

Where W is a widget type (such as JComponent or UIComponent) and M is a Metawidget type (such as SwingMetawidget or UIMetawidget).

processWidget is called for each widget built by the WidgetBuilders. WidgetProcessors can modify the given widget according to the given elementName (which is typically just 'property' or 'action' from the inspection-result) and the various attributes (as parsed from the inspection-result). They can use the given metawidget to help them if needed (e.g. 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 shown in Figure 2.6.

Typical WidgetProcessor list

Figure 2.6. Typical WidgetProcessor list


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 entirely. This new widget will then be passed down the list as shown in Figure 2.7. For an example use of this capability, see HiddenFieldProcessor.

WidgetProcessors can substitute widgets

Figure 2.7. WidgetProcessors can substitute widgets


Alternatively, the WidgetProcessor can decide to exclude the widget entirely by returning null. Subsequent WidgetProcessors will not be called, as shown in Figure 2.8, and no widget will be added to the Layout.

WidgetProcessors can exclude widgets

Figure 2.8. WidgetProcessors can exclude widgets


2.5.2 Usage

Some Metawidgets will instantiate default WidgetProcessors. This default behaviour can be overridden either in code:

metawidget.addWidgetProcessor( new MyWidgetProcessor() );

Or via metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<swingMetawidget xmlns="java:org.metawidget.swing">
	<widgetProcessors>
		<array>
			<myWidgetProcessor xmlns="java:com.myapp"/>
		</array>
	</widgetProcessors>
</swingMetawidget>

This allows easy plugging in of alternate WidgetProcessors. Note that overriding the default means the default is no longer instantiated. In the example above, this would mean MyWidgetProcessor is used but the default WidgetProcessors are not.

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 WidgetProcessor<JComponent, SwingMetawidget>() {
	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 e ) {
				someObject.doSomething();
			}
		}	
	}
}

2.5.3 Advanced Interface

The WidgetProcessor interface only has a single method. This allows it to take advantage of future Java language features such as:

final Object someObject = ...;

metawidget.addWidgetProcessor(
	#(JComponent w, String name, Map<String, String> attr, SwingMetawidget m)
	{ w.add( #(ActionEvent e) { someObject.doSomething() } ) } );

However for those needing more control over the WidgetProcessor lifecycle there is an extended interface AdvancedWidgetProcessor. This interface defines two additional methods:

void onStartBuild( M metawidget );
				
void onEndBuild( M metawidget );
onStartBuild and onEndBuild are called once, processWidget is called for each widget

Figure 2.9. onStartBuild and onEndBuild are called once, processWidget is called for each widget


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.

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.

2.5.4 Defaults

Some Metawidgets have default WidgetProcessors. Overriding the default means the default is no longer instantiated. This may not be what you want, so you may want to instantiate 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
<array>
	<simpleBindingProcessor />
	<reflectionBindingProcessor />
	<disabledAttributeProcessor />
</array>
AngularJS
[
	new metawidget.widgetprocessor.IdProcessor(),
	new metawidget.widgetprocessor.PlaceholderAttributeProcessor(),
	new metawidget.widgetprocessor.DisabledAttributeProcessor(),
	new metawidget.angular.widgetprocessor.AngularWidgetProcessor(...)
]
GWT StyleNameProcessor
JavaScript
[
	new metawidget.widgetprocessor.IdProcessor(),
	new metawidget.widgetprocessor.RequiredAttributeProcessor(),
	new metawidget.widgetprocessor.PlaceholderAttributeProcessor(),
	new metawidget.widgetprocessor.DisabledAttributeProcessor(),
	new metawidget.widgetprocessor.SimpleBindingProcessor()
]
JQuery UI
[
	new metawidget.widgetprocessor.IdProcessor(),
	new metawidget.widgetprocessor.RequiredAttributeProcessor(),
	new metawidget.widgetprocessor.PlaceholderAttributeProcessor(),
	new metawidget.widgetprocessor.DisabledAttributeProcessor(),
	new metawidget.jqueryui.widgetprocessor.JQueryUIBindingProcessor(),	
	new metawidget.widgetprocessor.SimpleBindingProcessor()
]
JSF
<array>
	<requiredAttributeProcessor />
	<immediateAttributeProcessor />
	<standardBindingProcessor />
	<readableIdProcessor />
	<labelProcessor />
	<standardValidatorProcessor />
	<standardConverterProcessor />
	<cssStyleProcessor />							
</array>
JSP (none)
Spring
<array>
	<pathProcessor />
	<cssStyleProcessor />							
</array>
Struts
<array>
	<nameProcessor />
	<cssStyleProcessor />							
</array>
Swing
<array>
	<reflectionBindingProcessor />
</array>
SWT
<array>
	<reflectionBindingProcessor />
</array>
Vaadin
<array>
	<simpleBindingProcessor />
	<reflectionBindingProcessor />
	<captionProcessor />
	<requiredProcessor />
	<minimumMaximumValidatorProcessor />
</array>

2.5.5 Immutability

All WidgetProcessors are required to be 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.5.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 (see Section 2.7, “metawidget.xml and ConfigReader”):

<beansBindingProcessor xmlns="java:org.metawidget.swing.widgetprocessor.binding.beansbinding"
	config="BeansBindingProcessorConfig">
	<updateStrategy>
		<enum>READ_WRITE</enum>
	</updateStrategy>
</beansBindingProcessor>

2.5.6 Implementing Your Own WidgetProcessor

Here is an example of a custom WidgetProcessor to add tooltips to JComponents. It extends the code from the tutorial (see Section 1.1, “Part 1 (Java version) - 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.iface.*;

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, 250 );
		frame.setVisible( true );
	}

	static class TooltipProcessor
		implements WidgetProcessor<JComponent, SwingMetawidget> {
		
		public JComponent processWidget( JComponent widget, String elementName,
										 Map<String, String> attributes, SwingMetawidget metawidget ) {
			widget.setToolTipText( attributes.get( NAME ) );
			return widget;
		}
	}
}

Like Inspectors, InspectionResultProcessors, WidgetBuilders and Layouts, WidgetProcessors are required to be immutable. However they will generally need to store some internal state, such as tracking the prefix for the tooltip. This can be achieved in two ways:

  1. For state that will remain constant throughout the life of the WidgetProcessor, such as the prefix for the tooltip, use 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 in metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

    <tooltipProcessor xmlns="java:com.foo" config="TooltipProcessorConfig">
    	<prefix>
    		<string>Tip:</string>
    	</prefix>
    </tooltipProcessor>
    [Important]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.
    [Tip]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.
  2. For state that will change across multiple widgets, such as BeansBindingProcessor which tracks all bindings so that it can implement a rebind method, store it in the Metawidget that is passed to onStartBuild, processWidget and onEndBuild. You may want to further wrap the state in a small helper class. For example:

    public JComponent processWidget( JComponent widget, String elementName,
    										Map<String, String> attributes, SwingMetawidget metawidget ) {
    		
    	getState( container ).bindings.add( attributes.get( NAME ));
    }
    
    private State getState( SwingMetawidget metawidget ) {
    	State state = (State) metawidget.getClientProperty( getClass() );
    
    	if ( state == null ) {
    		state = new State();
    		metawidget.putClientProperty( getClass(), state );
    	}
    
    	return state;
    }
    
    static class State {
    	List<String> bindings;
    	...other state variables...
    }

    You could also consider storing the state in a ThreadLocal. This is straightforward because WidgetProcessors are not re-entrant.

2.6 Layouts

Layouts arrange widgets on the screen, following their building by a WidgetBuilder and processing by any WidgetProcessors. This section covers Layouts in general. For in-depth documentation of individual Layouts see Chapter 8, Layouts.

2.6.1 Interface

All Layouts must implement the Layout interface. This is a simple interface that defines only one method:

void layoutWidget(W widget,String elementName,Map<String,String> attributes,C container,M metawidget);

Where W is a widget type (such as JComponent or UIComponent) and M is a Metawidget type (such as SwingMetawidget or UIMetawidget).

Where W is a widget type (i.e. Control, JComponent etc), C is widget container type (i.e. Composite, JComponent etc) and M is a Metawidget type (i.e. SwtMetawidget, SwingMetawidget etc). Don't be put off by having so many parameterized types! Metawidget needs them so that it can present a consistent API across many architectures. However this complication doesn't impact your own custom plugins because you're able to substitute concrete, platform-specific values for each parameter (i.e. Control, Composite, SwtMetawidget ).

layoutWidget is called for each widget. Layouts should add the given widget as a child of the given container, according to the given elementName (which is typically just 'property' or 'action' from the inspection-result) and the various attributes (as parsed from the inspection-result). They can use the given metawidget to access additional services if needed (such as state saving beween invocations, see Section 2.6.7, “Implementing Your Own Layout”).

[Tip]layoutWidget is called 'in-place'
layoutWidget is called immediately after WidgetBuilder.buildWidget and WidgetProcessor.processWidget, and before the next widget is generated. An alternate design would be to 'collect' all widgets generated by buildWidget and processWidget, then iterate over them separately for the layout. If you prefer this approach, you can simulate it by having layoutWidget do nothing but 'remember' each widget, then iterate over them in endContainerLayout (see Section 2.6.2, “Advanced Interface”). However not all UI frameworks allow this approach, because they do not suport widgets being instantiated independent of a layout, nor moved between layouts (e.g. SWT).

2.6.2 Advanced Interface

The Layout interface only has a single method. However for those needing more control over the Layout lifecycle there is an extended interface AdvancedLayout. This interface defines four additional methods:

void onStartBuild(M metawidget);
				
void startContainerLayout(W container, M metawidget);
				
void endContainerLayout(W container, M metawidget);

void onEndBuild(M metawidget);
startContainerLayout and endContainerLayout are called for each container, layoutWidget is called for each widget

Figure 2.10. startContainerLayout and endContainerLayout are called for each container, layoutWidget is called for each widget


The first method, onStartBuild, is called at the start of the widget building process, before the WidgetBuilder is called. Layouts may wish to act on this event to initialize themselves ready for processing, or to perform 'outermost-container-only' processing, such as adding toolbar facets. However it is acceptable to do nothing.

The second method, startContainerLayout, is called to initialize the given container. It is acceptable to do nothing.

The third method, endContainerLayout, is called to finish the given container. It is acceptable to do nothing.

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. Layouts may wish to act on this event to clean themselves up following processing, or to perform 'outermost-container-only' processing, such as adding status bar facets. However it is acceptable to do nothing.

2.6.3 LayoutDecorator

LayoutDecorator allows you to combine multiple Layouts together in a heirarchy. Conceptually, this is similar to CompositeInspector or CompositeWidgetBuilder, but Layouts are fundamentally different in that most are 'end points' that cannot sensibly be composed into sequential lists (i.e. what should happen if you try to combine a GridBagLayout with a FlowLayout?).

Rather, Layouts must be combined heirarchically, with an 'outer' Layout delegating to a single 'inner' Layout. LayoutDecorator is an abstract class that can be extended in order to decorate other Layouts. For example, GridBagLayout can be decorated using TabbedPaneLayoutDecorator to add tabbed section functionality.

<metawidgetLayout>
	<tabbedPaneLayoutDecorator xmlns="java:org.metawidget.swing.layout"
		config="TabbedPaneLayoutDecoratorConfig">
		<layout>
			<gridBagLayout />
		</layout>
	</tabbedPaneLayoutDecorator>			
</metawidgetLayout>

A LayoutDecorator can also decorate another LayoutDecorator to provide fine-grained control over nested sections. For example, the domain object...

public class Person {
	@UiSection( { "Person", "Name" } )
	public String firstname;
	
	public String surname;
	
	@UiSection( { "Person", "Contact Detail" } )
	public String telephone;
}

...could be rendered using nested TabbedPaneLayoutDecorators...

<metawidgetLayout>
	<tabbedPaneLayoutDecorator xmlns="java:org.metawidget.swing.layout"
		config="TabbedPaneLayoutDecoratorConfig">
		<layout>
			<tabbedPaneLayoutDecorator config="TabbedPaneLayoutDecoratorConfig">
				<layout>
					<gridBagLayout />
				</layout>
			</tabbedPaneLayoutDecorator>			
		</layout>
	</tabbedPaneLayoutDecorator>			
</metawidgetLayout>

...as shown in Figure 2.11.

Nested TabbedPaneLayoutDecorators

Figure 2.11. Nested TabbedPaneLayoutDecorators


Alternatively, it could use a TabbedPaneLayoutDecorator nested within a SeparatorLayoutDecorator...

<metawidgetLayout>
	<separatorLayoutDecorator xmlns="java:org.metawidget.swing.layout"
		config="SeparatorLayoutDecoratorConfig">
		<layout>
			<tabbedPaneLayoutDecorator config="TabbedPaneLayoutDecoratorConfig">
				<layout>
					<gridBagLayout />
				</layout>
			</tabbedPaneLayoutDecorator>			
		</layout>
	</separatorLayoutDecorator>			
</metawidgetLayout>

...as shown in Figure 2.12.

TabbedPaneLayoutDecorator within a SeparatorLayoutDecorator

Figure 2.12. TabbedPaneLayoutDecorator within a SeparatorLayoutDecorator


Or the opposite - a SeparatorLayoutDecorator nested within a TabbedPaneLayoutDecorator...

<metawidgetLayout>
	<tabbedPaneLayoutDecorator xmlns="java:org.metawidget.swing.layout"
		config="TabbedPaneLayoutDecoratorConfig">
		<layout>
			<separatorLayoutDecorator config="SeparatorLayoutDecoratorConfig">
				<layout>
					<gridBagLayout />
				</layout>
			</separatorLayoutDecorator>			
		</layout>
	</tabbedPaneLayoutDecorator>			
</metawidgetLayout>

...as shown in Figure 2.13.

SeparatorLayoutDecorator within a TabbedPaneLayoutDecorator

Figure 2.13. SeparatorLayoutDecorator within a TabbedPaneLayoutDecorator


2.6.4 Usage

Unless explicitly specified, each Metawidget will instantiate a default Layout. This default behaviour can be overridden either in code:

metawidget.setLayout( new MyLayout() );

Or via metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<swingMetawidget xmlns="java:org.metawidget.swing">
	<layout>
		<myLayout xmlns="java:com.myapp"/>
	</layout>
</swingMetawidget>

This allows easy plugging in of alternate Layouts.

2.6.5 Defaults

All Metawidgets have default Layouts. 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
<textViewLayoutDecorator config="TextViewLayoutDecoratorConfig">
	<layout>
		<tableLayout />
	</layout>
</textViewLayoutDecorator>
GWT LabelLayoutDecorator around a FlexTableLayout
JavaScript (incl. AngularJS and JQuery UI)
new metawidget.layout.HeadingTagLayoutDecorator(
	new metawidget.layout.TableLayout()
)
JSF
<outputTextLayoutDecorator config="OutputTextLayoutDecoratorConfig">
		<layout>
			<simpleLayout/>
		</layout>
	</outputTextLayoutDecorator>
JSP
<headingTagLayoutDecorator config="HeadingTagLayoutDecoratorConfig">
	<layout>
		<htmlTableLayout/>
	</layout>
</headingTagLayoutDecorator>
Spring
<headingTagLayoutDecorator config="HeadingTagLayoutDecoratorConfig">
	<layout>
		<springTableLayout/>
	</layout>
</headingTagLayoutDecorator>
Struts
<headingTagLayoutDecorator config="HeadingTagLayoutDecoratorConfig">
	<layout>
		<htmlTableLayout/>
	</layout>
</headingTagLayoutDecorator>
Swing
<separatorLayoutDecorator config="SeparatorLayoutDecoratorConfig">
	<layout>
		<gridBagLayout/>
	</layout>
</separatorLayoutDecorator>
SWT
<separatorLayoutDecorator config="SeparatorLayoutDecoratorConfig">
	<layout>
		<gridLayout/>
	</layout>
</separatorLayoutDecorator>
Vaadin
<headingTagLayoutDecorator config="HeadingTagLayoutDecoratorConfig">
	<layout>
		<formLayout/>
	</layout>
</separatorLayoutDecorator>

2.6.6 Immutability

All Layouts are required to be immutable. This means you only need a single instance of a Layout for your entire application. If you are using metawidget.xml then ConfigReader takes care of this for you, but if you are instantiating Layouts in Java code you should reuse instances.

Note that immutable only means Layouts cannot be changed once instantiated - it does not mean they cannot be configured. Many Layouts have corresponding xxxConfig classes that allow them to be configured prior to instantation in a type-safe way. For example, an HtmlTableLayout can be configured in code:

metawidget.setLayout( new HtmlTableLayout( new HtmlTableLayoutConfig().setNumberOfColumns( 2 ));

Or in metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

<htmlTableLayout xmlns="java:org.metawidget.jsp.tagext.html.layout"
	config="HtmlTableLayoutConfig">
	<numberOfColumns>
		<int>2</int>
	</numberOfColumns>
</htmlTableLayout>

2.6.7 Implementing Your Own Layout

Here is an example of a custom Layout that arranges components in a bulleted HTML list. It could be useful for, say, arranging action elements that were represented by HTML anchor tags:

package com.myapp;

import java.util.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import org.metawidget.jsp.*;
import org.metawidget.jsp.tagext.*;
import org.metawidget.layout.iface.*;

public class HtmlListLayout
	implements AdvancedLayout<Tag, MetawidgetTag> {
	
	public void onStartBuild( MetawidgetTag metawidgetTag ) {}

	public void startContainerLayout( Tag containerTag, MetawidgetTag metawidgetTag ) {
		try {
			JspWriter writer = metawidgetTag.getPageContext().getOut();
			writer.write( "<ul>" );
		} catch ( Exception e ) {
			throw LayoutException.newException( e );
		}
	}

	public void layoutChild( Tag tag, String elementName, Map<String, String> attributes,
		Tag containerTag, MetawidgetTag metawidgetTag ) {
		try {
			JspWriter writer = metawidgetTag.getPageContext().getOut();
			writer.write( "<li>" );
			writer.write( JspUtils.writeTag( metawidgetTag.getPageContext(), tag, containerTag, null ) );
			writer.write( "</li>" );
		} catch ( Exception e ) {
			throw LayoutException.newException( e );
		}
	}

	public void endContainerLayout( Tag containerTag, MetawidgetTag metawidgetTag ) {
		try {
			JspWriter writer = metawidgetTag.getPageContext().getOut();
			writer.write( "</ul>" );
		} catch ( Exception e ) {
			throw LayoutException.newException( e );
		}
	}
	
	public void onEndBuild( MetawidgetTag metawidgetTag ) {}	
}

Like Inspectors, InspectionResultProcessors, WidgetBuilders and WidgetProcessors, Layouts are required to be immutable. However they will generally need to store some internal state, such as tracking the current row in a table layout. This can be achieved in two ways:

  1. For state that will remain constant throughout the life of the Layout, such as a CSS class to put on a generated HTML table, use xxxLayoutConfig classes. For example:

    public class HtmlTableLayoutConfig {
    	private String mTableStyle;
    	
    	public HtmlTableLayoutConfig setTableStyle( String tableStyle )	{
    		mTableStyle = tableStyle;
    		return this;
    	}
    
    	public String getTableStyleClass() {
    		return mTableStyleClass;
    	}
    	
    	// ...must override equals and hashCode too...
    }

    These xxxLayoutConfig classes are then passed to the Layout at construction time, and stored internally:

    public class HtmlTableLayout {
    	private String mTableStyle;
    	
    	public HtmlTableLayout() {
    		this( new HtmlTableLayoutConfig() );
    	}
    
    	public HtmlTableLayout( HtmlTableLayoutConfig config ) {
    		mTableStyle = config.getTableStyle();
    	}
    }

    This mechanism can then be controlled either programmatically:

    metawidget.setLayout( new HtmlTableLayout( new HtmlTableLayoutConfig().setTableStyleClass("foo")));

    Or in metawidget.xml (see Section 2.7, “metawidget.xml and ConfigReader”):

    <htmlTableLayout xmlns="java:org.metawidget.jsp.tagext.html.layout" config="HtmlTableLayoutConfig">
    	<tableStyleClass>
    		<string>foo</string>
    	</tableStyleClass>
    </htmlTableLayout>
    [Important]Config classes must override equals and hashCode
    If you want your configurable Layout to be cacheable and reusable by ConfigReader and metawidget.xml, the xxxLayoutConfig class must override equals and hashCode.
    [Tip]Generate an XML Schema
    If you intend your Layout 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 Layout 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.
  2. For state that will change during laying out, such as tracking the current row, store it in the Metawidget or container that is passed to onStartBuild, startContainerLayout, layoutChild, endContainerLayout and onEndBuild. You may want to further wrap the state in a small helper class, for example:

    public void layoutChild( JComponent component, String elementName, Map<String, String> attributes,
    								JComponent container, SwingMetawidget metawidget ) {
    	getState( container ).currentRow++;
    }
    
    private State getState( SwingMetawidget metawidget ) {
    	State state = (State) container.getClientProperty( getClass() );
    
    	if ( state == null ) {
    		state = new State();
    		container.putClientProperty( getClass(), state );
    	}
    
    	return state;
    }
    
    static class State {
    	int currentRow;
    	...other state variables...
    }

    You could also consider storing the state in a ThreadLocal. If you do this, be aware that because Layouts can decorate each other, they can be called in a re-entrant fashion inside the same Thread.

2.7 metawidget.xml and ConfigReader

metawidget.xml is an alternate (and optional) way to configure Metawidget. It allows you to configure a Java-based Metawidget without writing any Java code. This can be useful in environments with intermediate languages that shield the developer from the raw Java, such as JSPs or Facelets. It can also be useful as a single place for configuring multiple Metawidgets, such as across multiple dialogs of a Swing application. It is not applicable in pure JavaScript environments.

The metawidget.xml format, as parsed by org.metawidget.config.ConfigReader, is specialised for configuring Metawidget instances. The following sections explore some of the features of the XML format and ConfigReader.

2.7.1 Constructing New Objects

ConfigReader can construct new instances of objects. The XML element name is the Java class name and the XML namespace (xmlns) is the Java package. The following example constructs an org.metawidget.swing.SwingMetawidget.

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

	<swingMetawidget xmlns="java:org.metawidget.swing"/>
	
</metawidget>			

Using the XML namespace to denote the Java package allows the (optional) plugging in of XML Schema validation on a per-package basis. For example:

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd
						java:org.metawidget.swing http://metawidget.org/xsd/org.metawidget.swing-1.0.xsd">

	<swingMetawidget xmlns="java:org.metawidget.swing"/>
	
</metawidget>			

This allows you to validate the XML in your IDE, at development time.

2.7.2 Calling Setter Methods

Within an object, ConfigReader can call setXXX methods. The following example calls the setOpaque method of SwingMetawidget:

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

	<swingMetawidget xmlns="java:org.metawidget.swing">
		<opaque>
			<boolean>true</boolean>
		</opaque>
	</swingMetawidget>
	
</metawidget>

Multi-parameter methods are also supported. The following example calls the setParameter method of HtmlMetawidget (it takes two arguments):

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

	<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
		<parameter>
			<string>tableStyleClass</string>
			<string>table-form</string>
		</parameter>		
	</htmlMetawidget>
	
</metawidget>

2.7.3 Constructing Primitive Types

When calling setXXX methods the XML format can specify simple types. The previous example used boolean and string. Also supported are:

Element name Java type
<array> constructs a Java array. The array's component type (i.e. String[], int[] etc) is based on the signature of the method being invoked (i.e. setWidgetProcessors( WidgetProcessor... ))
<boolean> Java boolean primitive
<bundle> uses ResourceBundle.getBundle to construct a ResourceBundle
<class> Java Class
<constant> Static field. This can either be fully qualified (e.g. javax.swing.SwingConstants.LEFT) or just the field name, in which case the field must be defined by the class of the parent XML node
<enum> Java enum primitive. The enum type is based on the signature of the method being invoked
<file> uses FileInputStream to open a file as an InputStream
<format> constructs a java.text.MessageFormat
<instanceOf> uses Class.forName(...).newInstance() to construct an instance of the given Class
<int> Java int primitive
<list> constructs a java.util.ArrayList
<null> Java null value
<pattern> constructs a java.text.Pattern
<resource> uses Class.getResourceAsStream to open a resource as an InputStream
<set> constructs a java.util.HashSet
<string> constructs a java.lang.String
<url> uses URL.openStream to open a URL as an InputStream

2.7.4 Resolving Resources

Some environments store their resources in specialized locations that are inaccessible by normal means (i.e. ClassLoader.getResource). For example, Web environments use a WEB-INF folder that must be accessed through ServletContext.getResource. Simiarly, Android environments must resolve resources using Context.getResources.

ResourceResolver and its specialized subclasses, such as ServletResourceResolver understand this distinction and provide resource resolving capability to all the objects they create. Specifically, ConfigReader takes a ResourceResolver and passes it to any xxxConfig classes that implement NeedsResourceResolver.

2.7.5 Understanding Immutability

Metawidget dictates all Inspectors, InspectionResultProcessors, WidgetBuilders, WidgetProcessors and Layouts be immutable. This is an important design decision as it means a single instance can be reused across an entire application. Immutabilty is enforced by not having any setXXX methods on the classes themselves. Rather, the setXXX methods are called on Config objects, which are then passed to the class' constructor. Once constructed, the object cannot be changed.

ConfigReader understands this distinction by way of a config attribute. The following example configures an immutable Inspector. The setInspectors method is called on org.metawidget.inspector.composite.CompositeInspectorConfig and then passed to CompositeInspector:

<compositeInspector xmlns="java:org.metawidget.inspector.composite" config="CompositeInspectorConfig">
	<inspectors>
		<array>
			<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
			<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation" />
		</array>
	</inspectors>
</compositeInspector>

Having constructed an immutable object, ConfigReader will cache the instance and reuse it. The config attribute defaults to using the same package as the xmlns (i.e. org.metawidget.inspector.composite in the example above). This can be overridden if a fully qualified classname is provided. For example:

<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
	config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	...
</propertyTypeInspector>
[Important]Config classes must override equals and hashCode
In order to reliably cache and reuse an immutable object that uses a config attribute, the xxxConfig class must override equals and hashCode. This is important to bear in mind when implementing your own custom classes.

Caching and reusing instances works both when configuring multiple Metawidgets, and configuring multiple settings for a single Metawidget. For example:

<inspectors>
	<array>
		<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
			config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
			<propertyStyle>
				<javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean"
					config="JavaBeanPropertyStyleConfig">
					<supportPublicFields>
						<boolean>false</boolean>
					</supportPublicFields>
					<privateFieldConvention>
						<format>'m'{1}</format>
					</privateFieldConvention>
				</javaBeanPropertyStyle>
			</propertyStyle>						
		</propertyTypeInspector>
		<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"
			config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
			<propertyStyle>
				<javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean"
					config="JavaBeanPropertyStyleConfig">
					<supportPublicFields>
						<boolean>false</boolean>
					</supportPublicFields>
					<privateFieldConvention>
						<format>'m'{1}</format>
					</privateFieldConvention>
				</javaBeanPropertyStyle>
			</propertyStyle>						
		</metawidgetAnnotationInspector>
	</array>
</inspectors>

Here the same javaBeanPropertyStyle instance will be reused twice. However you still have to duplicate all javaBeanPropertyStyle's settings in two places. Instead metawidget.xml understands a shorthand:

<inspectors>
	<array>
		<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
			config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
			<propertyStyle>
				<javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean"
					config="JavaBeanPropertyStyleConfig" id="myPropertyStyle">
					<supportPublicFields>
						<boolean>false</boolean>
					</supportPublicFields>
					<privateFieldConvention>
						<format>'m'{1}</format>
					</privateFieldConvention>
				</javaBeanPropertyStyle>
			</propertyStyle>						
		</propertyTypeInspector>
		<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"
			config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
			<propertyStyle>
				<javaBeanPropertyStyle refId="myPropertyStyle" />
			</propertyStyle>						
		</metawidgetAnnotationInspector>
	</array>
</inspectors>

This shorthand can help remove duplication from your metawidget.xml.

3. Metawidgets

Metawidget ships with native widgets for different UI frameworks. Whilst all Metawidgets are broadly similar, they are tailored to take advantage of their native environment. This chapter covers each Metawidget in detail. For an explanation of the overall architecture of Metawidget, see Chapter 2, Architecture.

3.1 Desktop Metawidgets

Metawidget supports multiple desktop frameworks, so that you can choose the one that matches your preferred UI environment.

3.1.1 SwingMetawidget

SwingMetawidget is a Swing JComponent. For an introduction to SwingMetawidget, see Section 1.1, “Part 1 (Java version) - The First Metawidget Application” and Section 1.3.1, “Desktop Address Book”.

Installation

There are two steps to installing SwingMetawidget within a Swing application:

  1. Add metawidget-all.jar to your CLASSPATH.

  2. Optionally configure the Metawidget, as described below.

Configuration

SwingMetawidget is preconfigured with sensible defaults for Swing. You can change this configuration either programmatically (as detailed in Section 1.1.5, “Inspectors”) or using a metawidget.xml file (as detailed in Section 1.1.9, “Configuring Metawidget Externally”).

Customizing Look and Feel

Since inception, Swing has had built-in, and extensive, Look and Feel support. Metawidget does not overlap this. For layouts, Swing supports a multitude of LayoutManagers. Metawidget leverages these, and automates them to construct UIs automatically. The layout manager can be configured through either metawidget.xml or programmatically using setMetawidgetLayout.

Internationalization

SwingMetawidget supports localization through the setBundle method. This method takes a ResourceBundle object. Keys are looked up based on the name of each business property.

Under The Hood

SwingMetawidget inspects objects and populates child widgets automatically in response to the standard JComponent.paintComponent event. There is no need to explicitly trigger population. SwingMetwidget also populates itself just-in-time if clients interrogate it through standard methods such as JComponent.getComponentCount or JComponent.getBounds.

3.1.2 SwtMetawidget

SwtMetawidget is an SWT Composite. For an introduction to SwtMetawidget, see Section 1.3.1, “Desktop Address Book”.

Installation

There are two steps to installing SwtMetawidget within an SWT application:

  1. Add metawidget-all.jar to your CLASSPATH.

  2. Optionally configure the Metawidget, as described below.

Configuration

SwtMetawidget is preconfigured with sensible defaults for SWT. You can change this configuration either programmatically (as detailed for Swing in Section 1.1.5, “Inspectors”, but the SWT API is the same) or using a metawidget.xml file (as detailed in Section 1.1.9, “Configuring Metawidget Externally”).

Customizing Look and Feel

Since inception, SWT has had built-in, and extensive, Look and Feel support. Metawidget does not overlap this. For layouts, SWT supports a multitude of LayoutManagers. Metawidget leverages these, and automates them to construct UIs automatically. The layout manager can be configured through either metawidget.xml or programmatically using setMetawidgetLayout.

Internationalization

SwtMetawidget supports localization through the setBundle method. This method takes a ResourceBundle object. Keys are looked up based on the name of each business property.

3.2 Web Metawidgets

Metawidget supports multiple Web frameworks, so that you can choose the one that matches your preferred UI environment.

3.2.1 AngularJS Metawidget

metawidget-angular.js is a pure AngularJS Metawidget. For an introduction to metawidget-angular.js, see Section 1.3.2, “Web Address Book”.

Installation

There are three steps to installing metawidget-angular.js within a web application:

  1. Add metawidget-angular.min.js into /lib/metawidget/angular and metawidget-core.min.js into /lib/metawidget/core.

  2. Add two script tags to the top of your page:

    <script src="lib/metawidget/core/metawidget-core.min.js" type="text/javascript"></script>
    <script src="lib/metawidget/angular/metawidget-angular.min.js" type="text/javascript"></script>
  3. Add the 'metawidget' module to your AngularJS application:

    angular.module( 'myApp', [ 'metawidget' ]);
  4. Optionally configure the Metawidget, as described below.

Usage

metawidget-angular.js is an AngularJS directive. It can be used simply by:

<metawidget ng-model="foo" />

For compatibility with Internet Explorer 8, a namespaced version of the directive is also available:

<mw:metawidget ng-model="foo" />

For more information on IE8 compatibility see this page. The AngularJS Address Book sample application has also been made compatible with IE8.

Configuration

metawidget-angular.js is preconfigured with sensible defaults. You can change this configuration by putting a JavaScript object in your $scope and referring to it in the <metawidget> tag:

$scope.metawidgetConfig = {	
	inspector: ..., // Configure Inspector
	inspectionResultProcessors: [...], // Configure InspectorResultProcessors
	widgetBuilder: ..., // Configure WidgetBuilder
	widgetProcessors: [...], // Configure WidgetProcessors
	layout: ... // Configure Layout
};
<metawidget ng-model="..." config="metawidgetConfig" />

For advanced use cases, you can pass an array of configs. These will be applied in order:

<metawidget ng-model="..."
		configs="[metawidgetConfig1, metawidgetConfig2]" />

Two-Way Binding

metawidget-angular.js leverages Angular's native two-way binding. The ng-model, read-only and config attributes of the <metawidget> tag are all automatically watched for updates.

For example, if you set the read-only attribute to a variable in your scope, then update that variable based on a button click (say, 'Edit'), Metawidget will automatically re-render the widgets from read-only mode to editable mode. Equally, if you set the ng-model to a variable that is asynchronously loaded, Metawidget will automatically update as soon as the data becomes available. For an example of both these techniques, see the Angular version of the Section 1.3.2, “Web Address Book”.

The config attribute of the <metawidget> tag is similarly automatically watched for updates. Note the configs attribute (with an 's') is not automatically watched. This is because Angular has problems watching array types.

3.2.2 GwtMetawidget

GwtMetawidget is a client-side, JavaScript widget for GWT. For an introduction to GwtMetawidget, see Section 1.3.2, “Web Address Book” and Section 1.4.10, “GWT Hosted Mode Examples”.

Installation

There are five steps to installing Metawidget within a GWT application:

  1. Update the application's .gwt.xml module to include Metawidet:

    <module>								
    	<inherits name="org.metawidget.GwtMetawidget" />
    	...
    </module>
  2. Include both metawidget-all.jar and additional/gwt/metawidget-all-sources.jar in the CLASSPATH during the GWTCompiler phase. This provides the GwtMetawidget component source.

  3. Add metawidget-all.jar into WEB-INF/lib. This provides the GwtRemoteInspectorImpl servlet.

  4. Update the application's web.xml to include GwtRemoteInspectorImpl:

    <web-app>
    	...
    	<servlet>
    		<servlet-name>metawidget-inspector</servlet-name>
    		<servlet-class>org.metawidget.inspector.gwt.remote.server.GwtRemoteInspectorImpl</servlet-class>
    	</servlet>
    
    	<servlet-mapping>
    		<servlet-name>metawidget-inspector</servlet-name>
    		<url-pattern>/metawidget-inspector</url-pattern>
    	</servlet-mapping>
    </web-app>
  5. Optionally configure the Metawidget, as described below.

A working example of all these steps can be found in addressbook-gwt.war included in the examples distribution. You may also find the src/examples/gwt/addressbook Maven POM and the src/examples/gwt/clientside Maven POM useful.

Configuration

GwtMetawidget is preconfigured with sensible defaults for GWT. You can change this configuration either programmatically or by creating a metawidget.xml file in WEB-INF and adding an init-param to your web.xml:

<init-param>
	<param-name>config</param-name>
	<param-value>metawidget.xml</param-value>
</init-param>

Reflection and Annotations

GwtMetawidget leverages Metawidget's separate Inspector/renderer architecture and AJAX to perform server-side inspection as in Figure 3.1. This allows GwtMetawidget to reflect properties and inspect annotations of business objects, even though JavaScript supports neither.

GwtMetawidget uses AJAX to perform server-side inspection

Figure 3.1. GwtMetawidget uses AJAX to perform server-side inspection


The process is:

  1. instantiate the domain object on the client-side as normal (i.e. as JavaScript)

  2. give the domain object to GwtMetawidget (a client-side, JavaScript GWT Widget)

  3. GwtMetawidget uses AJAX to pass the business object to the server

  4. the server, using Java, runs all the Inspectors (including reflection and annotations)

  5. the server returns the inspection results as an XML document

  6. GwtMetawidget uses JavaScript to render the HTML widgets

Note that steps 3 and 5 (the AJAX call to and from the server) are the most costly in terms of performance. Techniques to improve GWT performance are discussed in Section 9.5.4, “Rebinding”.

Client-Side Inspection

As noted in the section called “Reflection and Annotations” by default GwtMetawidget uses server-side inspectors. This allows the full power of Java-based reflection but carries the performance cost of an AJAX call. This cost can be mitigated by using rebinding (see Section 9.5.4, “Rebinding”), but there is another way: inspection can be performed client-side, with no AJAX calls.

Setting up a client-side Inspector is very easy. The default GwtMetawidget Inspector is GwtRemoteInspectorProxy, which is itself a client-side Inspector (one that makes a remote call to GwtRemoteInspectorImpl). To replace this default, simply implement your own Inspector:

public class MyClientSideInspector
	implements Inspector {
	public String inspect( Object toInspect, String type, String... names ) {
		return ...some XML string...
	}
}

Make sure this Inspector is located under the client folder of your GWT application so that it is compiled by the GWTCompiler into JavaScript. Use this Inspector by doing...

myGWTMetawidget.setInspector( new MyClientSideInspector() )

...which overrides the default GwtRemoteInspectorProxy. For an example of this technique see Section 1.4.9, “GWT Client Side Example”.

Customizing Look and Feel

GWT Widgets do not distinguish between what a widget is versus how it is rendered. Instead, GwtMetawidget mimics this by providing loosely coupled Layout classes. Each Layout can further be configured by using LayoutConfig classes and standard CSS.

Internationalization

GwtMetawidget supports localization through the setDictionaryName method. This method takes a String containing the name of a JavaScript variable declared in the host HTML page. For example:

<script type="text/javascript">
	var bundle = {
	"add": "Add",
	"addBusiness": "Add Business Contact",
	"addPersonal": "Add Personal Contact"
};</script>

Keys are looked up based on the name of each business property.

3.2.3 JavaScript Metawidget

metawidget.js is a pure JavaScript Metawidget. For an introduction to metawidget.js, see Section 1.2, “Part 1 (JavaScript version) - The First Metawidget Application”.

Installation

There are two steps to installing metawidget.js within a web application:

  1. Add metawidget-core.min.js into /lib/metawidget/core.

  2. Add a script tag to the top of your page:

    <script src="lib/metawidget/core/metawidget-core.min.js" type="text/javascript"></script>
  3. Optionally configure the Metawidget, as described below.

Configuration

metawidget.js is preconfigured with sensible defaults. You can change this configuration by passing a JavaScript object to the metawidget.Metawidget constructor:

var mw = new metawidget.Metawidget( myElement, {	
	inspector: ..., // Configure Inspector
	inspectionResultProcessors: [...], // Configure InspectorResultProcessors
	widgetBuilder: ..., // Configure WidgetBuilder
	widgetProcessors: [...], // Configure WidgetProcessors
	layout: ... // Configure Layout
} );

For advanced use cases, you can pass an array of configs. These will be applied in order:

var mw = new metawidget.Metawidget( myElement,
				[metawidgetConfig1, metawidgetConfig2] );

Configs support the following attributes:

Attribute
inspector Set the Inspector to use
prependInspectionResultProcessors Insert InspectionResultProcessors before any existing ones. Can be an array or a single InspectionResultProcessor
inspectionResultProcessors Set the list of InspectionResultProcessors (replacing any existing ones)
appendInspectionResultProcessors Append InspectionResultProcessors after any existing ones. Can be an array or a single InspectionResultProcessor
widgetBuilder Set the WidgetBuilder to use
prependWidgetProcessors Insert WidgetProcessors before any existing ones. Can be an array or a single WidgetProcessor
widgetProcessors Set the list of WidgetProcessors (replacing any existing ones)
appendWidgetProcessors Append WidgetProcessors after any existing ones. Can be an array or a single WidgetProcessor
layout Set the Layout to use
styleClass Set the CSS class of the top-level Metawidget, as well as any nested Metawidgets. This can be useful for adjusting the spacing of nested Metawidgets

Customizing Look and Feel

Look and feel can be customized using CSS. In addition, metawidget.js provides loosely coupled Layout classes. Some Layouts can further be configured by passing a JavaScript object:

new metawidget.layout.TableLayout( {
	numberOfColumns: 2,
	tableStyleClass: "my-table"
} );

Events

metawidget.js will fire a buildEnd event upon completion of each build. Interested parties can use this to perform any special processing after the Metawidget is completely built.

Note that, for many use cases, clients may be better implementing WidgetBuilder.onEndBuild, WidgetProcessor.onEndBuild or Layout.onEndBuild instead. For example:

{
	onStartBuild: function( mw ) {
		// perform work before any widgets processed
	},					
	processWidget: function( widget, elementName, attributes, mw ) {
		// process each widget
	},
	onEndBuild: function( mw ) {
		// perform work after all widgets processed
	}
}

Or for layouts:

{
	onStartBuild: function( mw ) {
		// perform work before any widgets laid out
	},
	startContainerLayout: function( container, mw ) {
		// perform work for new container
	},
	layoutWidget: function( widget, elementName, attributes, container, mw ) {
		// layout each widget
	},
	endContainerLayout: function( container, mw ) {
		// perform work to close container
	},
	onEndBuild: function( mw ) {
		// perform work after all widgets laid out
	}
}

Inspection Results

Internally, metawidget.js uses JSON Schema as its inspection result format.

Web Component

metawidget.js will automatically register itself as an HTML 5 Web Component using document.registerElement if available. This allows developers to use Metawidget with very little configuration. For example:

<!DOCTYPE HTML>
<html>
	<head>
		<script src="metawidget-core.min.js"></script>
		<style>
			#metawidget {
				border: 1px solid #cccccc;
				width: 250px;
				border-radius: 10px;
				padding: 10px;
				margin: 50px auto;
				display: block;
			}
		</style>
		<script>
			var person = {
				firstname: 'Homer',
				surname: 'Simpson',
				age: 36,
				save: function() {
					document.getElementById( 'metawidget' ).save();
					console.log( person );
				}
			}
		</script>
	</head>
	<body>
		<x-metawidget id="metawidget" path="person"></x-metawidget>
	</body>
</html>

The x-metawidget tag supports the following attributes:

Attribute
path Path of the object to inspect. The path will be resolved in the global scope and supports simple namespace paths such as foo.bar
config Optional path of an object to use to configure the Metawidget. Supports simple namespace paths such as foo.bar
readonly Optional. Can be true or false

The x-metawidget tag also supports the following methods:

Method
save Saves the current values in the UI back into the object defined by path using SimpleBindingProcessor. This is a convenience method. To access other Metawidget APIs, clients can use getMetawidget instead
getMetawidget Access the underlying Metawidget APIs

3.2.4 JQuery UI Metawidget

metawidget-jqueryui.js wraps the JavaScript Metawidget into a JQuery UI component. For an introduction to metawidget.js, see Section 1.2, “Part 1 (JavaScript version) - The First Metawidget Application”.

Installation

There are three steps to installing metawidget-jqueryui.js within a web application:

  1. Add metawidget-core.min.js into /lib/metawidget/core and metawidget-jqueryui.min.js into /lib/metawidget/jquery-ui

  2. Add a script tag to the top of your page:

    <script src="lib/metawidget/core/metawidget-core.min.js" type="text/javascript"></script>
    <script src="lib/metawidget/jquery-ui/metawidget-jqueryui.min.js" type="text/javascript"></script>
  3. Wrap Metawidget around a DOM element:

    $('#metawidget').metawidget();
    $('#metawidget').metawidget('buildWidgets', toInspect);
  4. Optionally configure the Metawidget, as described below.

Configuration

metawidget-jqueryui.js is preconfigured with sensible defaults. You can change this configuration either at construction time:

$('#metawidget').metawidget( {
	layout: new metawidget.layout.SimpleLayout()
} );

Or by passing JQuery UI options:

$('#metawidget').metawidget('option','layout',new metawidget.layout.SimpleLayout());

3.2.5 Node.js

Metawidget tries hard not to dictate your architecture. So although metawidget.js is predominantly a client-side technology, there are no restrictions on using it for server-side UI generation if you prefer.

Metawidget comes packaged as a Node.js module. Install it using:

npm install metawidget

The Metawidget module must be used in combination with a DOM implementation. This can either be jsdom, envjs, or even a simple implementation of your own (see test/render.js inside the Metawidget module for an example). Metawidget must be wrapped around a DOM element. The Metawidget constructor takes this element, and thereafter always uses element.ownerDocument rather than referencing any global document object.

See test/render.js inside the npm module for a full example how to use Metawidget. But briefly:

var metawidget = require( 'metawidget' );
...
var mw = new metawidget.Metawidget( yourElement );
mw.toInspect = {
	firstname: "Joe",
	surname: "Bloggs"
};
mw.buildWidgets();
...
// yourElement is now populated with child components

3.2.6 HtmlMetawidgetTag (JSP)

HtmlMetawidgetTag is a JSP taglib. For an introduction to HtmlMetawidgetTag, see Section 1.3.2, “Web Address Book”.

Installation

There are three steps to installing HtmlMetawidgetTag within a JSP application:

  1. Add metawidget-all.jar into WEB-INF/lib.

  2. Add a tag library descriptor to the top of your JSP page:

    <%@ taglib uri="http://metawidget.org/html" prefix="m" %>
    ...
    <m:metawidget value="foo"/>
    ...

    Note that plain JSP doesn't provide any automatic management of domain objects. You must manually add objects (foo in the example above) into the scope. This can be done in several ways. The Address Book sample application uses embedded Java code:

    <%
    	...
    	request.setAttribute( "contactSearch", contactSearch );
    	...
    %>

    Alternatively, you could use the useBean tag:

    <jsp:useBean id="foo" class="com.myapp.Foo"/>
  3. Optionally configure the Metawidget, as described below.

Configuration

HtmlMetawidgetTag is preconfigured with sensible defaults for JSP. You can change this configuration by creating a metawidget.xml file. By default HtmlMetawidgetTag looks for this file in WEB-INF, but you can configure its location in web.xml:

<context-param>
	<param-name>org.metawidget.jsp.tagext.CONFIG_FILE</param-name>
	<param-value>config/metawidget.xml</param-value>
</context-param>

Alternatively, you can specify an explicit config file at the tag level:

<m:metawidget value="foo" config="alternate-metawidget.xml"/>

Customizing Look and Feel

JSP tag libraries do not distinguish between what a widget is versus how it is rendered. Instead, HtmlMetawidgetTag mimics this by providing loosely coupled Layout classes. Each Layout can further be configured by using LayoutConfig classes and standard CSS.

Internationalization

HtmlMetawidgetTag supports localization through a ResourceBundle set at the tag level:

<fmt:setBundle basename="org.metawidget.shared.allwidgets.resource.Resources" var="bundle"/>
<m:metawidget bundle="${bundle}"/>

Keys are looked up based on the name of each business property.

Overriding Widget Creation

With regard to overriding widget creation, JSP lacks some component-based features. Specifically, whilst it is possible for JSP tags to reference their parent (using TagSupport.findAncestorWithClass), they have no way to enumerate their children. Therefore, it is not possible to directly support arbitrary child tags within HtmlMetawidgetTag. As a next best thing, Metawidget includes StubTag for wrapping arbitrary tags. It also supports wrapping arbitrary HTML. For example:

<m:metawidget value="contact">

	<m:stub value="communications">
		<table class="data-table">
			...
		</table>
	</m:stub>

</m:metawidget>

For an example, see Section 1.3.2, “Web Address Book”.

3.2.7 UIMetawidget (JSF)

UIMetawidget is a Java Server Faces component. For an introduction to UIMetawidget, see Section 1.3.2, “Web Address Book” and Section 1.4.2, “Seam Example”.

Installation

There are four steps to installing UIMetawidget within a JSF application:

  1. Add metawidget-all.jar into WEB-INF/lib.

  2. Add a tag library descriptor to the top of your JSP page...

    <%@ taglib uri="http://metawidget.org/faces" prefix="m" %>
    ...
    <m:metawidget value="#{foo}"/>
    ...

    ...or Facelets page...

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
    	xmlns:m="http://metawidget.org/faces">
    ...
    <m:metawidget value="#{foo}"/>
    ...
  3. Optionally configure the Metawidget, as described below.

Configuration

UIMetawidget is preconfigured with sensible defaults for JSF. You can change this configuration by creating a metawidget.xml file. By default UIMetawidget looks for this file in WEB-INF, but you can configure its location in web.xml:

<context-param>
	<param-name>org.metawidget.faces.component.CONFIG_FILE</param-name>
	<param-value>config/metawidget.xml</param-value>
</context-param>

Alternatively, you can specify an explicit config file at the tag level:

<m:metawidget value="#{foo}" config="alternate-metawidget.xml"/>

Or you can use JSF's binding attribute to configure Metawidget programmatically:

<m:metawidget value="#{foo}" binding="#{myBean.metawidget}"/>

Then in your managed bean:

public class MyManagedBean {

	public UIMetawidget getMetawidget() {

		// First-time init
		//
		// JSF spec: "When a component instance is first created (typically by virtue of being
		// referenced by a UIComponentELTag in a JSP page), the JSF implementation will retrieve the
		// ValueExpression for the name binding, and call getValue() on it. If this call returns a
		// non-null UIComponent value (because the JavaBean programmatically instantiated and
		// configured a component already), that instance will be added to the component tree that
		// is being created"

		UIMetawidget metawidget = new HtmlMetawidget();
		initMetawidget( metawidget );
		return metawidget;
	}

	public void setMetawidget( UIMetawidget metawidget ) {

		// POST-back init
		//
		// JSF spec: "When a component tree is recreated during the Restore View phase of
		// the request processing lifecycle, for each component that has a ValueExpression
		// associated with the name 'binding', setValue() will be called on it, passing the
		// recreated component instance"

		initMetawidget( metawidget );
	}
	
	private void initMetawidget( UIMetawidget metawidget ) {
	
		...configure Metawidget programmatically...
	}	
}

This approach can also be used to inspect Objects and Classes directly, without a value binding:

<m:metawidget binding="#{myBean.metawidget}"/>

Then in your managed bean:

public class MyManagedBean {

	...as previous example...
		
	private void initMetawidget( UIMetawidget metawidget ) {
	
		metawidget.setValue( MyBusinessObject.class );
	}	
}

Customizing Look and Feel

One of JSF's most important Look and Feel technologies is CSS. Metawidget supports several approaches to suit different needs.

By convention, JSF's HTML widgets (HtmlInputText, HtmlSelectBooleanCheckbox, etc) define style and styleClass attributes for applying CSS styles and classes to their output. CssStyleProcessor follows this convention. When expanding to a single widget (such as an HtmlInputText) the style and styleClass attributes are applied to it. When expanding to multiple widgets, all widgets have the same style and styleClass attributes applied to them. This can be useful as a way to 'tag' every widget.

Another important JSF Look and Feel technology is Renderers. Whilst often Renderers are discussed in the context of rendering the same widget to different platforms (e.g. HTML or WML), they can equally be used to render the same widget to the same platform but in different ways. Renderers can be configured either per tag:

<m:metawidget ... rendererType="div"/>

Or globally in metawidget.xml:

<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
	<rendererType>
		<string>myRenderer</string>
	</rendererType>
	...
</htmlMetawidget>

HtmlTableLayoutRenderer is the default LayoutRenderer. It further defines parameters such as tableStyle, labelStyle and columnClasses (see the the section called “HtmlTableLayoutRenderer” for a complete list). The latter is a comma separated list of CSS style classes to be applied to table columns. The first style class is the label column, the second the widget column, and the third the 'required' column. Further style classes may be used for multi-column layouts. You can get quite far using, for example:

.table-component-column input { width: 100%; }

..this approach has the advantage of automatically applying to every widget, so overridden widgets do not have to explicitly set styleClass information. However, not all Web browsers support fine-grained CSS selectors such as...

.table-component-column input[type="button"] { width: auto; }

...in which case it may be better to switch to using styleClass on HtmlMetawidget itself.

Other supplied LayoutRenderers include div and simple (see Section 8.2.3, “JSF Layouts” for a complete list).

Internationalization

UIMetawidget supports localization through a ResourceBundle set either at the application level in faces-config.xml:

<faces-config>
	<application>
		<message-bundle>com.myapp.resource.Resources</message-bundle>
	</application>					
</faces-config>

Or set at the tag level:

<m:metawidget bundle="#{myBean.bundle}"/>

Collections

By default, JSF's UIData component supports rendering data of type java.util.List, javax.faces.model.DataModel or Java arrays. UIMetawidget's HtmlWidgetBuilder respects this and will generate an HtmlDataTable for each type. It will decide UIColumns based on inspecting the List or array's component type for properties marked required (if any).

This will give a basic out-of-the-box representation of a Collection, but it is expected developers will ultimately define their own WidgetBuilder to represent a Collection to their preference. This may include: supporting java.util.Set; allowing inline editing; supporting pagination and so on. For more information, see this blog entry.

Facets

JSF f:facet tags can be used within m:metawidget tags to adorn layouts. The exact name and interpretation of the facet is at the discretion of the Layout. For example HtmlTableLayoutRenderer recognizes:

<m:metawidget value="...">
	<f:facet name="header">
		...instructions could go here...
	</f:facet>
	<f:facet name="footer">
		...button bar could go here...
	</f:facet>
</m:metawidget>

There is a special case when the m:metawidget's value resolves directly to a single widget, rather than iterating over properties and creating multiple sub-widgets (eg. if value is pointed directly at a String property it will resolve to an HtmlInputText). In this case any facets (and converters and validators) apply not to the Layout but to the generated widget. This can be useful to declaratively attach behaviours:

<m:metawidget value="#{foo.name}">
	<a4j:support event="onclick" onsubmit="alert('onclick')"/>
</m:metawidget>

Or:

<m:metawidget value="#{foo.name}">
	<f:validator validatorId="myValidator"/>
</m:metawidget>

If you are generating a single widget but still want the facet to apply to the Layout, wrap it in a parent m:metawidget that has no value:

<m:metawidget>
	<m:metawidget value="#{foo.name}" rendererType="simple"/>
	<f:facet name="footer">
		...button bar could go here...
	</f:facet>
</m:metawidget>

A parent m:metawidget with no value simply acts as a way to lay out children.

JSF 2.0

UIMetawidget supports JSF 2.0. It automatically detects whether JSF 2.0 classes are available (specifically javax.faces.event.PreRenderViewEvent) and switches to using them. However not all JSF 2.0 implementations properly support PreRenderViewEvent. You need at least Mojarra 2.1.7 (specifically a version that includes this fix) or MyFaces 2.0.3 (specifically a version that includes this fix). If you need to work with older versions, you must...

  • disable partial state saving by setting javax.faces.PARTIAL_STATE_SAVING to false in web.xml

  • disable PreRenderViewEvent support by setting org.metawidget.faces.component.DONT_USE_PRERENDER_VIEW_EVENT to true in web.xml; and

...but even then, Metawidget exercises the dynamic capabilites of JSF implementations more than most. If you start experiencing strange behaviour (such as components being re-ordered following POST back) upgrade to the latest version of JSF your environment permits.

By default, UIMetawidget does not rebuild widgets upon an AJAX request unless the Metawidget's Id is explicitly included in the list of execute Ids. There are several reasons for this:

  • Suppose a Metawidget X has children A, B and C. If B is executed by an AJAX request, this will trigger X with a PreRenderViewEvent (because it is the parent). But if X rebuilds A and C, and they weren't part of the execute request, their values will be lost. This is similar to how UIMetawidget doesn't rebuild upon a validation error

  • Similarly, if the Metawidget's backing bean is request-scoped, rebuilding A and C will mean they fetch their values from a new (likely empty) backing bean instance. There will be no opportunity for A and C to postback their values first (because they are not executed)

  • Some components (such as RichFaces' UIAutocomplete) do not allow fine-grained control over what is executed and rendered. They just execute and render themselves

  • AJAX is about performance, so typically clients are not wanting to rebuild large sections of the component tree

Although this default behaviour is safer it does, however, result in less dynamic UIs. Clients can use setBuildWidgetsOnAjaxRequest to override the default behaviour and instruct UIMetawidget to always rebuild widgets upon an AJAX request. Mechanisms such as conversation-scoped backing beans can be used to avoid losing values.

3.2.8 SpringMetawidgetTag

SpringMetawidgetTag is a Spring taglib. For an introduction to SpringMetawidgetTag, see Section 1.3.2, “Web Address Book”.

Installation

There are three steps to installing SpringMetawidgetTag within a Spring application:

  1. Add metawidget-all.jar into WEB-INF/lib.

  2. Add a tag library descriptor to the top of your JSP page:

    <%@ taglib uri="http://metawidget.org/spring" prefix="m" %>
    ...
    <m:metawidget path="fooCommand"/>
    ...
  3. Optionally configure the Metawidget, as described below.

Configuration

SpringMetawidgetTag is preconfigured with sensible defaults for Spring. You can change this configuration by creating a metawidget.xml file. By default SpringMetawidgetTag looks for this file in WEB-INF, but you can configure its location in web.xml:

<context-param>
	<param-name>org.metawidget.jsp.tagext.CONFIG_FILE</param-name>
	<param-value>config/metawidget.xml</param-value>
</context-param>

Alternatively, you can specify an explicit config file at the tag level:

<m:metawidget path="fooCommand" config="alternate-metawidget.xml"/>

Customizing Look and Feel

JSP tag libraries do not distinguish between what a widget is versus how it is rendered. Instead, SpringMetawidgetTag mimics this by providing loosely coupled Layout classes. Each Layout can further be configured by using LayoutConfig classes.

Internationalization

SpringMetawidgetTag supports localization through a ResourceBundle set either at the application level in servlet.xml:

<beans>
	<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename">
        	<value>com.myapp.resource.Resources</value>
        </property>
    </bean>
</beans>

Or set at the tag level:

<fmt:setBundle basename="com.myapp.resource.Resources" var="bundle"/>
<m:metawidget bundle="${bundle}"/>

Overriding Widget Creation

With regard to overriding widget creation, JSP-based technologies such as Spring lack some component-based features. Specifically, whilst it is possible for JSP tags to reference their parent (using TagSupport.findAncestorWithClass), they have no way to enumerate their children. Therefore, it is not possible to directly support arbitrary child tags within SpringMetawidgetTag. As a next best thing, Metawidget includes StubTag for wrapping arbitrary tags. It also supports wrapping arbitrary HTML. For example:

<m:metawidget path="contactCommand">

	<m:stub path="communications">
		<table class="data-table">
			...
		</table>
	</m:stub>

</m:metawidget>

For an example, see Section 1.3.2, “Web Address Book”.

3.2.9 StrutsMetawidgetTag

StrutsMetawidgetTag is a Struts taglib. For an introduction to StrutsMetawidgetTag, see Section 1.3.2, “Web Address Book”.

Installation

There are three steps to installing StrutsMetawidgetTag within a Struts application:

  1. Add metawidget-all.jar into WEB-INF/lib.

  2. Add a tag library descriptor to the top of your JSP page:

    <%@ taglib uri="http://metawidget.org/struts" prefix="m" %>
    ...
    <m:metawidget property="fooForm"/>
    ...
  3. Optionally configure the Metawidget, as described below.

Configuration

StrutsMetawidgetTag is preconfigured with sensible defaults for Spring. You can change this configuration by creating a metawidget.xml file. By default StrutsMetawidgetTag looks for this file in WEB-INF, but you can configure its location in web.xml:

<context-param>
	<param-name>org.metawidget.jsp.tagext.CONFIG_FILE</param-name>
	<param-value>config/metawidget.xml</param-value>
</context-param>

Alternatively, you can specify an explicit config file at the tag level:

<m:metawidget property="fooForm" config="alternate-metawidget.xml"/>

Customizing Look and Feel

JSP tag libraries do not distinguish between what a widget is versus how it is rendered. Instead, StrutsMetawidgetTag mimics this by providing loosely coupled Layout classes. Each Layout can further be configured by using specific param tags.

Internationalization

StrutsMetawidgetTag supports localization through a ResourceBundle set either at the application level in struts-config.xml:

<struts-config>
	<message-resources parameter="com.myapp.resource.Resources" null="false"/>
</struts-config>

Or set at the tag level:

<fmt:setBundle basename="com.myapp.resource.Resources" var="bundle"/>
<m:metawidget bundle="${bundle}"/>

Overriding Widget Creation

With regard to overriding widget creation, JSP-based technologies such as Struts lack some component-based features. Specifically, whilst it is possible for JSP tags to reference their parent (using TagSupport.findAncestorWithClass), they have no way to enumerate their children. Therefore, it is not possible to directly support arbitrary child tags within StrutsMetawidgetTag. As a next best thing, Metawidget includes StubTag for wrapping arbitrary tags. It also supports wrapping arbitrary HTML. For example:

<m:metawidget property="contactForm">

	<m:stub property="communications">
		<table class="data-table">
			...
		</table>
	</m:stub>

</m:metawidget>

For an example, see Section 1.3.2, “Web Address Book”.

3.2.10 VaadinMetawidget

VaadinMetawidget is a Vaadin CustomComponent. For an introduction to VaadinMetawidget, see Section 1.3.2, “Web Address Book”.

Installation

There are two steps to installing VaadinMetawidget within a Vaadin application:

  1. Add metawidget-all.jar into WEB-INF/lib.

  2. Optionally configure the Metawidget, as described below.

Configuration

VaadinMetawidget is preconfigured with sensible defaults for Vaadin. You can change this configuration either programmatically or using a metawidget.xml file.

3.3 Mobile Metawidgets

Metawidget supports multiple mobile frameworks, so that you can choose the one that matches your preferred UI environment.

3.3.1 AndroidMetawidget

AndroidMetawidget is an Android View. For an introduction to AndroidMetawidget, see Section 1.3.3, “Mobile Address Book”.

Installation

There are two steps to installing AndroidMetawidget within an Android application:

  1. Add metawidget-all.jar to your CLASSPATH (i.e. under your project's Libraries tab in Eclipse)

  2. Optionally configure the Metawidget, as described below.

[Tip]Note
Given the resource constraints of a mobile device, consider creating a custom metawidget-all.jar that only includes the classes you need (as detailed in Section 9.5.1, “JAR Size”).

Configuration

AndroidMetawidget is preconfigured with sensible defaults for Android. You can change this configuration programmatically or by creating a metawidget.xml file in res/raw and specifying it at the tag level:

<view class="org.metawidget.android.widget.AndroidMetawidget" config="@raw/metawidget"/>

Customizing Look and Feel

AndroidMetawidget uses Android's standard res/values/styles.xml mechanism. Styles can be set either programmatically or passed inside metawidget.xml using the @ notation. For example:

<layout>
	<textViewLayoutDecorator xmlns="java:org.metawidget.android.widget.layout"
				config="TextViewLayoutDecoratorConfig">
		<style>
			<int>@org.metawidget.example.android.addressbook:style/section</int>
		</style>
	</textViewLayoutDecorator>
</layout>

Internationalization

AndroidMetawidget supports localization through the setBundle method. This method takes your R.string class, which is generated by Android from your res/values/strings.xml file. For example, if your strings.xml file was:

<resources>
	<string name="dob">Date of Birth<string>
</resources>

And your domain object had a property:

class Person {
	...
	public Date getDob() {
		return mDob;
	}
}

Then you can set Metawidget to use your R.string class either programmatically or using metawidget.xml:

<androidMetawidget>
	<bundle>
		<class>org.metawidget.example.android.addressbook.R$string</class>
	</bundle>
</androidMetawidget>

And at runtime Metawidget will translate the Person.getDob method into a property called dob, resolve the integer R.string.dob and use that to look up the localized text in strings.xml. For different locales, put the strings.xml file in adjacent res/values-xx folders - for example res/values-en.

Getting and Setting Values

By default, Android does not come with a binding mechanism to map View values to business object values. There are a number of third-party binding libraries, but many are either discontinued or rely on statically declared UIs. As a workaround, by default AndroidMetawidget supplies a SimpleBindingProcessor (see the section called “SimpleBindingProcessor”).

Alternatively, to map values manually, remove SimpleBindingProcessor and do:

metawidget.setValue( mPerson.getFirstname(), "firstname" );
mPerson.setFirstname( (String) metawidget.getValue( "firstname" ) );

For a complete working example, see Section 1.3.3, “Mobile Address Book”.

3.3.2 JQuery Mobile Metawidget

metawidget-jquerymobile.js wraps the JavaScript Metawidget into a JQuery Mobile component. For an introduction to metawidget.js, see Section 1.2, “Part 1 (JavaScript version) - The First Metawidget Application”.

Installation

There are three steps to installing metawidget-jquerymobile.js within an HTML 5 hybrid application:

  1. Add metawidget-core.min.js into /lib/metawidget/core and metawidget-jquerymobile.min.js into /lib/metawidget/jquery.mobile

  2. Add script tags to the top of your page:

    <script src="lib/metawidget/core/metawidget-core.min.js" type="text/javascript"></script>
    <script src="lib/metawidget/jquery.mobile/metawidget-jquerymobile.min.js" type="text/javascript"></script>
  3. Wrap Metawidget around a DOM element:

    $('#metawidget').metawidget();
    $('#metawidget').metawidget('buildWidgets', toInspect);
  4. Optionally configure the Metawidget, as described below.

Configuration

metawidget-jquerymobile.js is preconfigured with sensible defaults. You can change this configuration either at construction time:

$('#metawidget').metawidget( {
	layout: new metawidget.layout.SimpleLayout()
} );

Or by passing JQueryMobile options:

$('#metawidget').metawidget('option','layout',new metawidget.layout.SimpleLayout());

3.4 Static Metawidgets

Whilst Metawidget is primarily focussed on the runtime generation of UIs, there are some scenarios where static UI generation is more appropriate.

Static generation has a different set of advantages and disadvantages to runtime generation. For example it cannot be as dynamic in responding to the state of the system, such as changing the UI based on the security of the logged-in user. It also tends to insert a lot of lines of code into your project. On the other hand, static generation does not introduce additional runtime dependencies. It also has an easier learning curve when tweaking the generated UI.

Nothing about the Metawidget pipeline (see Chapter 2, Architecture) is explictly static or runtime-based. Therefore the static Metawidgets all use the same approach of pluggable WidgetBuilders, WidgetProcessors and so on, to configure your UI. The static Metawidgets even reuse the exact same Inspectors and InspectionResultProcessors. For example JpaInspector can be used to inspect JPA annotations on classes for either runtime or static scenarios.

3.4.1 StaticPropertyStyle

StaticPropertyStyle sits at the same conceptual level as, say, JavaBeanPropertyStyle or GroovyPropertyStyle. It allows Metawidget Inspectors to be repurposed to inspect static classes, as opposed to objects.

3.4.2 StaticMetawidget

StaticMetawidget is the base class for all static UI generation.

An important requirement for static code generation is the Metawidget should not rely on the technology being generated being available. For example, a static JSF Metawidget should not rely on FacesContext.getCurrentInstance. Architectually, this makes all static Metawidgets more similar to each other than to their corresponding runtime version. For example, the static JSF Metawidget is more similar to the static Spring Metawidget than it is to the runtime JSF Metawidget. Therefore, it makes sense to have a StaticMetawidget base class.

The base class concerns itself with manipulating instances of StaticWidgets. These represent fragments of static output (text, basically) that the Metawidget assembles. Subclasses of StaticWidget provide more domain-specific APIs. For example StaticXmlWidget handles XML fragments, including opening and closing tags and tracking namespaces. Equally StaticJavaWidget handles fragments of Java code, including opening and closing braces and tracking imports.

4. Inspectors

This chapter covers each Inspector in detail. For an explanation of how Inspectors fit into the overall architecture of Metawidget, see Chapter 2, Architecture

Throughout this chapter when we say 'returns the following attributes' this is a shorthand way of saying 'returns the following Metawidget attributes (via a piece of XML conforming to inspection-result.xsd). These are passed to the WidgetBuilder to assist with choosing appropriate widgets'. Quite which widget will be chosen is covered in Chapter 6, WidgetBuilders: it could be a JSF HtmlInputTextarea, or a Swing JTextArea, or some other widget from some other framework.

4.1 Composite Inspector

CompositeInspector (the name refers to the Composite design pattern) delegates inspection to one or more sub-inspectors, then combines the resulting DOMs. The combining algorithm works as follows. For each element:

  1. top-level elements (typically entity) with the same type attribute in both DOMs are combined

  2. child elements (typically property or action) with the same name attribute in both DOMs are combined

  3. the ordering of child elements (typically property or action) in the existing DOM is preserved. New child elements are added either at the end or immediately after the last combined child

  4. element attributes from the new DOM override ones in the existing DOM (e.g. hidden or read-only)

This algorithm should be suitable for most use cases, but one benefit of having a separate CompositeInspector is that developers can replace it with their own version, with its own combining algorithm, if required.

4.2 Property Inspectors

Being an OIM, a core strength of Metawidget is inspecting Objects. Metawidget is very flexible in this regard, with pluggable support for different languages and different implementation styles.

4.2.1 BaseObjectInspector

BaseObjectInspector underlies many of the Inspectors that inspect objects (as opposed to, say, XML files). It provides easy-to-override methods such as...

protected Map<String, String> inspectProperty( Property property )

...for inspecting properties, and...

protected Map<String, String> inspectAction( Action action )

...for inspecting actions, and finally...

protected Map<String, String> inspectTrait( Trait trait )

...for inspecting things that apply to both properties and actions (e.g. @UiLabel). Quite what constitutes a 'property' or an 'action' is decoupled into pluggable PropertyStyles and ActionStyles.

PropertyStyle

The PropertyStyle interface allows pluggable, fine-grained control over what is considered a 'property'. Different environments may have different approaches to defining what constitutes a property. For example, JavaBean-properties are convention-based, whereas Groovy has explicit property support. Equally, some environments may have framework-specific, base class properties that should be filtered out and excluded from the list of 'real' business model properties.

The default PropertyStyle is JavaBeanPropertyStyle. To change it within metawidget.xml:

<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
	config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<propertyStyle>
		<groovyPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.groovy"/>
	</propertyStyle>
</propertyTypeInspector>

To change it programmatically:

BaseObjectInspectorConfig config = new BaseObjectInspectorConfig();
config.setPropertyStyle( new GroovyPropertyStyle() );
metawidget.setInspector( new PropertyTypeInspector( config ) );
JavaBeanPropertyStyle

The JavaBeanPropertyStyle is the default PropertyStyle used by all BaseObjectInspector subclasses (which includes all annotation inspectors).

This PropertyStyle recognizes JavaBean-convention getXXX, setXXX and isXXX methods. In addition, it maintains a cache of reflected classes for performance.

When using getter methods with private members, make sure you annotate the getter/setter not the private field. By default, JavaBeanPropertyStyle does not find annotations on private fields, because the JavaBean specification does not define a way to determine which private fields belong to which getter/setter methods. However, many developers adopt a naming convention (e.g. getter isFoo maps to field mFoo) and you can specify your preferred convention using JavaBeanPropertyStyleConfig.setPrivateFieldConvention. This method takes a MessageFormat parameter which you can use to format the property name accordingly. For example:

Format Private Field Name
{0} dateOfBirth, surname
'm'{1} mDateOfBirth, mSurname
'm_'{0} m_dateOfBirth, m_surname

If you need more control over the mapping, consider extending JavaBeanPropertyStyle and overriding getPrivateField. You could also use JavaBeanPropertyStyleConfig.setSupportPublicFields( true ) to recognize public fields directly, though this is not recommended.

GroovyPropertyStyle

The GroovyPropertyStyle recognizes GroovyBean properties.

Groovy tries hard to make its GroovyBean properties compatible with JavaBean getters/setters, and indeed one can almost use the default JavaBeanPropertyStyle to read them. Unfortunately, GroovyBeans differ in that:

  • annotations defined on properties are only attached to the (generated) private member variable, not the (generated) getter/setter methods.

  • GroovyBeans define an implicit getMetaClass method which, although matching the JavaBean signature, should not be treated as a business model property.

JavassistPropertyStyle

The JavassistPropertyStyle extends JavaBeanPropertyStyle and makes use of Javassist for those environments that have it available.

Javassist is used to inspect the debug line numbering information embedded in JVM bytecode to sort getters/setters according to their original declaration order in the source code. This saves domain objects having to use @UiComesAfter (or an XML file, or some other method) to impose an ordering.

However, a danger of this approach is that if the domain objects are ever recompiled without debug line numbering information (e.g. when moving from development to production) the UI fields will lose their ordering. Such a subtle bug may not be picked up, so as a safeguard JavassistPropertyStyle 'fails hard' with an InspectorException if line numbers are not available.

JavassistPropertyStyle uses the following sorting algorithm:

  • superclass public fields come first, sorted by name (if JavaBeanPropertyStyleConfig.setSupportPublicFields has been set).

  • superclass methods come next, sorted by getter line number (or, if no getter, setter line number).

  • public fields come next, sorted by name (if JavaBeanPropertyStyleConfig.setSupportPublicFields has been set).

  • methods come last, sorted by getter line number (or, if no getter, setter line number).

Note this algorithm is less flexible than @UiComesAfter, which can interleave superclass and subclass properties. However, it is possible to use both @UiComesAfter and JavassistPropertyStyle together to get the best of both worlds.

ScalaPropertyStyle

The ScalaPropertyStyle recognizes Scala properties.

Scala can make its properties compatible with JavaBean getters/setters, but only if you put special @BeanProperty annotations on them. This can be quite onerous. Instead, ScalaPropertyStyle is designed to access Scala properties natively.

Implementing Your Own PropertyStyle

All PropertyStyles must implement the PropertyStyle interface. BasePropertyStyle assists in caching properties per class (looking them up is often expensive, involving reflection or similar techniques) and in excluding properties based on name, type or base class. Here is an example of a custom PropertyStyle that identifies properties based on ResourceBundle i18n entries. It extends the code from the tutorial (see Section 1.1, “Part 1 (Java version) - The First Metawidget Application”).

package com.myapp;
				
import java.util.*;
import javax.swing.*;
import org.metawidget.inspector.iface.*;
import org.metawidget.inspector.impl.*;
import org.metawidget.inspector.impl.propertystyle.*;
import org.metawidget.inspector.impl.propertystyle.javabean.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.swing.*;
import org.metawidget.util.*;

public class Main {

	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setInspector( new PropertyTypeInspector( new BaseObjectInspectorConfig()
			.setPropertyStyle( new BundlePropertyStyle() ) ) );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
	
	static class BundlePropertyStyle
		extends JavaBeanPropertyStyle {

		protected Map<String, Property> inspectProperties( String type ) {
			try {
				Class<?> clazz = ClassUtils.niceForName( type );
				Map<String, Property> properties = CollectionUtils.newTreeMap();
				ResourceBundle bundle = ResourceBundle.getBundle( "MyBundle" );

				for ( Enumeration<String> e = bundle.getKeys(); e.hasMoreElements(); ) {
					String key = e.nextElement();
					properties.put( key, new FieldProperty( key, clazz.getField( key ) ) );
				}

				return properties;
			}
			catch ( Exception ex ) {
				throw InspectorException.newException( ex );
			}
		}
	}
}

For brevity, this example extends JavaBeanPropertyStyle. Normally, you would want to extend BasePropertyStyle and, as well as overriding inspectProperties to locate the properties, implement the Property interface with mechanisms for interrogating the property.

[Tip]Note
In this particular example, it may be useful to create a BundlePropertyStyleConfig class that implements NeedsResourceResolver (see Section 2.7.4, “Resolving Resources”). Then it could use ResourceResolver.openResource to locate the bundle in case it was in a specialized location (such as WEB-INF/).

ActionStyle

The ActionStyle interface allows pluggable, fine-grained control over what is considered an 'action'.

Different environments may have different approaches to defining what constitutes an action. For example, the Swing AppFramework uses an @org.jdesktop.application.Action annotation.

The default ActionStyle is MetawidgetActionStyle. To change it within metawidget.xml:

<metawidgetAnnotationInspector config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<actionStyle>
		<swingAppFrameworkActionStyle xmlns="java:org.metawidget.inspector.impl.actionstyle.swing">
	</actionStyle>
</metawidgetAnnotationInspector>

To change it programmatically:

BaseObjectInspectorConfig config = new BaseObjectInspectorConfig();
config.setActionStyle( SwingAppFrameworkActionStyle.class );
metawidget.setInspector( new MetawidgetAnnotationInspector( config ) );

Note these ActionStyles only apply to BaseObjectInspector and its subclasses. This covers most annotation-recognising Inspectors (e.g. JpaInspector, HibernateValidatorInspector) but not XML-based inspectors. For example, PageflowInspector recognizes actions in JBoss jBPM pageflow XML files without using any ActionStyle.

MetawidgetActionStyle

The default Metawidget ActionStyle recognizes any method annotated with @UiAction. Action methods must not accept any parameters in their signature.

SwingAppFrameworkActionStyle

The SwingAppFrameworkActionStyle recognises Swing AppFramework's @Action annotation as denoting an action.

4.2.2 PropertyTypeInspector (Java version)

PropertyTypeInspector extends BaseObjectInspector, and so inherits its features. In addition, it returns the following attributes for the following business properties:

Metawidget Attribute Property Type
lookup lookup of 'true, false' if the type is Boolean. Values of enums, as returned by .name(), if the type is Enum
lookup-labels lookup of 'Yes, No' if the type is Boolean. This will generally be localized by the Metawidget. Labels of enums, as returned by .toString(), if the type is Enum
no-setter if the property has no setXXX method. Note no-setter is distinct from read-only, because it is common to have no setter for a complex type (e.g. Person.getAddress) but this shouldn't make all its contents (e.g. Address.getStreet) read-only.
no-getter if the property has no getXXX method
parameterized-type if the property is using generics
type declared type of the property
actual-type if the actual type differs from the declared type (i.e. it is a subclass)

4.2.3 JsonSchemaInspector (Java version)

Inspector to look for metadata in JSON Schema files. Consider using in conjunction with JsonSchemaTypeMappingProcessorConfig to convert JSON types into Java types.

public static void main( String[] args ) {

	// Metawidget

	Display display = new Display();
	Shell shell = new Shell( display );
	shell.setText( "JSON Viewer" );
	shell.setLayout( new FillLayout() );

	String jsonSchema = "{ properties: { \"firstname\": { \"type\": \"string\", \"required\": true }, ";
	jsonSchema += "\"age\": { \"type\": \"number\" }, ";
	jsonSchema += "\"notes\": { \"type\": \"string\", \"large\": true }}}";

	final SwtMetawidget metawidget = new SwtMetawidget( shell, SWT.None );
	metawidget.setInspector( new JsonSchemaInspector(
		new JsonInspectorConfig().setInputStream( new ByteArrayInputStream( jsonSchema.getBytes() ))));
	metawidget.addInspectionResultProcessor(
		new TypeMappingInspectionResultProcessor<SwtMetawidget>(
			new JsonSchemaTypeMappingProcessorConfig() ));
	metawidget.setInspectionPath( "personObject" );

	// Shell

	shell.setVisible( true );
	shell.open();

	while ( !shell.isDisposed() ) {
		if ( !display.readAndDispatch() ) {
			display.sleep();
		}
	}

	display.dispose();
}

4.2.4 JsonSchemaInspector (JavaScript version)

Inspects JSON Schemas for their properties. Because Metawidget already uses JSON Schema internally as its inspection result format, this Inspector does not need to do much. However it adds support for JSON schemas that contain nested schemas. For example:

<html>
	<head>
		<script src="angular.min.js"></script>
		<script src="metawidget-core.min.js"></script>
		<script src="metawidget-angular.min.js"></script>
		<script>
			angular.module( 'myApp', [ 'metawidget' ] );

			function UserController( $scope ) {

			$scope.user = {};

			$scope.config = {
				inspector: new metawidget.inspector.JsonSchemaInspector( {
					properties: {
					   title: { type: 'string', enum: ['Mr', 'Mrs', 'Ms'] },
					   name: { type: 'string' },
					   email: { type: 'string', required: true },
					   birthday: { type: 'date' },
					   password: { type: 'string', masked: true },
					   address: {
							properties: {
							   street: { type: 'string' },
							   city: { type: 'string' },
							   postcode: { type: 'string' }
							}
					   },
					   notes: { type: 'string', large: true }
					}
				} )
			}
		}
		</script>
		
	</head>
	<body ng-app="myApp" ng-controller="UserController">
		<metawidget ng-model="user" config="config"/>
	</body>
</html>

Furthermore, JsonSchemaInspector adds support for JSON Schemas that describe array items, through the standard items property. For example:

<html>
	<head>
		<script src="metawidget-core.min.js"></script>
		<style>
			#metawidget {
				border: 1px solid #cccccc;
				width: 250px;
				border-radius: 10px;
				padding: 10px;
				margin: 50px auto;
			}
		</style>
	</head>
	<body>
		<div id="metawidget"/>
		<script type="text/javascript">
			var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
				inspector: new metawidget.inspector.CompositeInspector( [
					new metawidget.inspector.PropertyTypeInspector(),
					new metawidget.inspector.JsonSchemaInspector( {
						properties: {
							family: {
								items: {
									properties: {
										id: {
											type: 'string',
											hidden: true
										},
										employer: {
											type: 'string'
										}
									}
								}
							}
						}
					} )
				] )
			} );
			mw.toInspect = {
				firstname: 'Homer',
				surname: 'Simpson',
				age: 36,
				family: [ {
					id: 0,
					firstname: 'Marge',
					surname: 'Simpson'
				}, {
					id: 1,
					firstname: 'Bart',
					surname: 'Simpson'
				} ]
			};
			mw.buildWidgets();
		</script>
	</body>
</html>

4.2.5 PropertyTypeInspector (JavaScript version)

The JavaScript PropertyTypeInspector is much simplified compared to its Java-counterpart. It inspects JavaScript objects for their property names and types.

In principal, ordering of property names within JavaScript objects is not guaranteed. In practice, most browsers respect the original order that properties were defined in. However you may want to combine PropertyTypeInspector with a custom Inspector that imposes a defined ordering using propertyOrder attributes.

4.3 Annotation Inspectors

Beyond the base issue of inspecting an object and its properties, a number of Inspectors are focused on third-party annotations. These annotation Inspectors all extend BaseObjectInspector, and so inherit its features, but in addition they inspect the following frameworks.

4.3.1 BeanValidationInspector

BeanValidationInspector inspects Bean Validation (JSR 303) annotations. It returns the following attributes for the following business properties:

Metawidget Attribute Bean Validation Annotation
maximum-fractional-digits @Digits(fraction=...)
maximum-integer-digits @Digits(integer=...)
maximum-length @Size(max=...)
maximum-value @Max
minimum-length @Size(min=...)
minimum-value @Min
required @NotNull

4.3.2 FacesAnnotationInspector

FacesAnnotationInspector inspects Java Server Faces-specific annotations. It returns the following attributes for the following business properties:

Metawidget Attribute Annotation
currency-code, currency-symbol, number-uses-grouping-separators, minimum-integer-digits, maximum-integer-digits, minimum-fractional-digits, maximum-fractional-digits, locale, number-pattern and number-type @UiFacesNumberConverter - annotates the field should use the standard JSF NumberConverter.

Note: the NumberConverter property integerOnly is not specified using this annotation, as it can be inferred from the property's type

date-style, locale, datetime-pattern, time-style, time-zone and datetime-type @UiFacesDateTimeConverter - annotates the field should use the standard JSF DateTimeConverter
faces-ajax-event @UiFacesAjax - annotates the widget for this field should use AJAX in response to the given event
faces-component @UiFacesComponent - annotates the field should be rendered using the given component type in the UI.

Use of this annotation does not bind the business class to the UI quite as tightly as it may appear, because JSF has a loosely coupled relationship between <component-type> and <component-class>, and a further loose coupling between <component> and <render-kit>, as defined in faces-config.xml

faces-converter UiFacesConverter - annotates the field should use the given JSF converter in the UI. This annotation uses the converter id, not its class. Alternatively, this annotation can be an EL expression that evaluates to a javax.faces.convert.Converter instance.
faces-lookup @UiFacesLookup - annotates the value in the field should belong to the Collection returned by the given EL expression
faces-suggest @UiFacesSuggest - annotates the value in the field should be 'suggested' (i.e. using a pop-up box) using the Collection returned by the given EL expression

4.3.3 HibernateValidatorInspector

HibernateValidatorInspector inspects Hibernate Validator annotations. It returns the following attributes for the following business properties:

Metawidget Attribute Hibernate Validator Annotation
maximum-fractional-digits @Digits(fractionalDigits=...)
maximum-integer-digits @Digits(integerDigits=...)
maximum-length @Length(max=...)
maximum-value @Max
minimum-length @Length(min=...)
minimum-value @Min
required @NotNull or @NotEmpty

4.3.4 JacksonInspector

JacksonInspector inspects Jackson annotations. It returns the following attributes for the following business properties:

Metawidget Attribute Jackson Annotation
hidden @JsonIgnore

4.3.5 JaxbInspector

JaxbInspector inspects Java API for XML Binding (JAXB) annotations. It returns the following attributes for the following business properties:

Metawidget Attribute JPA Annotation
hidden @XmlTransient
required @XmlElement(required=true)

4.3.6 JpaInspector

JpaInspector inspects Java Persistence API annotations. It returns the following attributes for the following business properties:

Metawidget Attribute JPA Annotation
hidden @Id, unless JpaInspectorConfig.setHideIds is false. @Version, unless JpaInspectorConfig.setHideVersions is false. @Transient, unless JpaInspectorConfig.setHideTransients is false.
large @Lob
maximum-length @Column(length=...)
required @Column(nullable=false) or @ManyToOne(optional=false)

4.3.7 MetawidgetAnnotationInspector

As much as possible, Metawidget tries to inspect metadata from existing sources, without introducing new concepts. Where that is not sufficient, MetawidgetAnnotationInspector adds a handful of annotations:

Metawidget Attribute Metawidget Annotation
(any) @UiAttributes and @UiAttribute - a 'catch all' for denoting arbitrary UI metadata
action @UiAction
comes-after @UiComesAfter
dont-expand @UiDontExpand - denotes a value should not be inspected and expanded into sub-widgets. This can be useful if, say, you have a read-only property and just want to display its top-level toString() rather than all its child properties
hidden @UiHidden - denotes a value should be hidden in the UI. The value may still be rendered on the client, depending on the Metawidget (i.e. for Web clients, may use a HTML hidden field)
label @UiLabel - denotes the label to use in the UI. Can be a resource key if the UI is using resource bundles, or an EL expression if the UI has an expression language (i.e. JSF)
large @UiLarge - denotes the field should be 'large' in the UI (i.e. a multi-line textbox)
lookup @UiLookup - denotes the value in the field should belong to the given set of Strings
masked @UiMasked - denotes a value should be masked in the UI (e.g. a password field)
read-only @UiReadOnly - denotes a value should be read-only in the UI
section @UiSection - denotes the start of a logical grouping in the UI. Subsequent properties are assumed to belong to the same section until a different section heading is encountered. Sections can be cancelled using a section heading with an empty String. Sections can be nested by specifying an array of section names.
wide @UiWide - denotes the field should be 'wide' in the UI, spanning all columns in a multi-column layout. 'Wide' is different to 'large', because 'large' implies a data size (i.e. BLOB or CLOB) whereas 'wide' refers purely to spanning columns. Generally all 'large' fields are implicitly 'wide', but not all 'wide' fields are 'large'. For example, you may want a normal text field (not a text area) to span all columns.

4.3.8 OvalInspector

OvalInspector inspects OVal annotations. It returns the following attributes for the following business properties:

Metawidget Attribute OVal Annotation
maximum-length @Length(max=...) or @MaxLength
maximum-value @Max or @Range(max=...)
minimum-length @Length(min=...) or @MinLength
minimum-value @Min or @Range(min=...)
required @NotNull or @NotEmpty or @NotBlank

4.4 XML Inspectors

Whilst we don't necessarily encourage the use of XML-based metadata, if you already have XML configuration files in your architecture Metawidget will take advantage of them. Equally, XML can be useful for declaring 'ad hoc' UI entities that do not map to any Java class, as well as for declaring UI-specific attributes for existing Java classes (i.e. if you prefer not to use annotations, or if you want to introduce additional 'virtual' properties).

Note when using XML-based metadata you should still try to avoid duplicating metadata that already exists in other parts of your application. For example, if you are also using PropertyTypeInspector in your CompositeInspector there is no need to duplicate the names and types of all properties.

Once nice feature of XML is that ordering of child elements (such as <property>) is explicit, so XML-based Inspectors make great 'first inspectors' for use within CompositeInspector (e.g. you don't need to also use @UiComesAfter).

4.4.1 BaseXmlInspector

BaseXmlInspector's config class, BaseXmlInspectorConfig, uses a setInputStream method to specify the location of the XML. This allows a variety of options for sourcing the XML. For example:

<xmlInspector xmlns="java:org.metawidget.inspector.xml"
	config="XmlInspectorConfig">
	<inputStream>
		<resource>com/myapp/metawidget-metadata.xml</resource>
	</inputStream>
</xmlInspector>

And:

<xmlInspector xmlns="java:org.metawidget.inspector.xml"
	config="XmlInspectorConfig">
	<inputStream>
		<url>http://myserver.com/my-xml.xml</url>
	</inputStream>
</xmlInspector>

As well as specifying multiple files (which will all be merged and inspected as one):

<xmlInspector xmlns="java:org.metawidget.inspector.xml"
	config="XmlInspectorConfig">
	<inputStreams>
		<array>
			<url>http://myserver.com/my-xml-1.xml</url>
			<url>http://myserver.com/my-xml-2.xml</url>
		</array>			
	</inputStreams>
</xmlInspector>

This functionality is extended to all XML-based Inspectors.

Mixing XML and Object-based Inspectors

Several pieces of BaseXmlInspector functionality apply to mixing XML-based Inspectors (e.g. XmlInspector) and Object-based Inspectors (e.g. PropertyTypeInspector) in the same application (i.e. via CompositeInspector).

First, you may encounter a problem whereby the Object-based Inspectors will always stop at null or recursive references, whereas the XML Inspectors (which have no knowledge of Object values) will continue. This can lead to the WidgetBuilders constructing a UI for a null Object, which may upset some WidgetProcessors (e.g. BeansBindingProcessor). To resolve this, you can set BaseXmlInspectorConfig.setRestrictAgainstObject, whereby the XML-based Inspector will do a check for null or recursive references, and not return any XML. In addition, setting restrictAgainstObject allows the XML Inspector to traverse child relationships and infer their types using the Object. This saves having to explicitly specify those types and relationships in the XML.

Second, by default you need to explicitly specify any inheritance relationships between types in the XML, because the XML has no knowledge of your Java classes. This includes the names of any proxied classes. If this becomes laborious, you can set BaseXmlInspectorConfig.setInferInheritanceHierarchy to infer the relationships automatically from your Java classes. If you are using setRestrictAgainstObject, setInferInheritanceHierarchy is implied.

Third, it is important the properties defined by the XML and the ones defined by the Java classes stay in sync. To enforce this, you can set BaseXmlInspectorConfig.setValidateAgainstClasses.

4.4.2 CommonsValidatorInspector

CommonsValidatorInspector inspects Apache Commons Validator validation.xml files. It returns the following attributes for the following business properties:

Metawidget Attribute Validator XML
maximum-length <field depends="maxlength" /><var><var-name>maxlength</var-name>...
maximum-value <field depends="intRange" /><var><var-name>max</var-name>... (or floatRange or doubleRange)
minimum-length <field depends="minlength" /><var><var-name>minlength</var-name>...
minimum-value <field depends="intRange" /><var><var-name>min</var-name>... (or floatRange or doubleRange)
required <field depends="required" />

4.4.3 HibernateInspector

HibernateInspector inspects Hibernate hibernate.cfg.xml and mapping.hbm.xml files. For the former, it iterates over <session-factory>'s <mapping> elements and inspects all mapping files. It returns the following attributes for the following business properties:

Metawidget Attribute Hibernate XML
hidden <id />
large <property type="clob" />
maximum-length <property length="..." />
parameterized-type <bag type="..." /> or <list type="..." /> or <set type="..." />
required <property not-null="true" />

4.4.4 PageflowInspector

PageflowInspector inspects JBoss jBPM pageflow files looking for page nodes and their associated transitions to be used as actions. For example, this pageflow.jpdl.xml file...

<page name="confirm">
	<transition name="purchase" to="complete" />
	<transition name="cancel" to="cancel" />
</page>

...would return purchase and cancel as available actions for the confirm page. For an example of PageflowInspector in use, see Section 1.4.4, “jBPM Example”.

4.4.5 SeamInspector

SeamInspector inspects Seam XML files for useful metadata. Specifically:

  • Delegates jbpm:pageflow-definitions elements from components.xml to PageflowInspector.

4.4.6 XmlInspector

XmlInspector inspects inspection-result-1.0.xsd-compliant files (such as metawidget-metadata.xml).

XmlInspector is a very simple Inspector: it takes as its input XML in the same format that Inspectors usually output. It can be useful for declaring 'ad hoc' UI entities that do not map to any Java class, as well as for declaring UI-specific attributes for existing Java classes (ie. if you prefer not to use annotations, or if you want to introduce additional 'virtual' properties). Some attributes accept multiple values, such as lookup. These can be supplied as a comma-separated string. The values will be trimmed for whitespace. If the values themselves contain commas, they can be escaped with the \ character.

Note when using XmlInspector you should still try to avoid duplicating UI metadata that already exists in other parts of your application. For example, if you are also using PropertyTypeInspector in your CompositeInspector there is no need to duplicate the names and types of properties. Also, if you are using PropertyTypeInspector and XmlInspector together, please read the documentation for restrictAgainstObject.

XmlInspector does add some niceties beyond inspection-result-1.0.xsd. It supports an extends attribute to allow one entity to inherit from another. It also supports nested entities, for example:

<entity type="Person">
   <property name="surname"/>
   <property name="address">
      <property name="street"/>
      <property name="postcode"/>
   </property>
</entity>

4.4.7 XmlSchemaInspector

XmlSchemaInspector inspects XML Schema (XSD) files. If used for a Java environment, consider using in conjunction with XmlSchemaToJavaTypeMappingProcessor. For returning results to JavaScript environments, consider JavaToJavaScriptTypeMappingProcessor and XmlUtils.elementToJson.

4.4.8 WsdlInspector

WsdlInspector inspects Web Services Description Language (WSDL) files. It extracts the XML Schema from the <schema> element of the WSDL and parses it for useful metadata.

If WsdlInspector is used for a Java environment, consider using it in conjunction with XmlSchemaToJavaTypeMappingProcessor. For returning results to JavaScript environments, consider JavaToJavaScriptTypeMappingProcessor and XmlUtils.elementToJson.

5. InspectionResultProcessors

This chapter covers each InspectionResultProcessor in detail. For an explanation of how InspectionResultProcessors fit into the overall architecture of Metawidget, see Chapter 2, Architecture

5.1 ComesAfterInspectionResultProcessor

ComesAfterInspectionResultProcessor sorts inspection results according to the comes-after attribute. This attribute can be created using the @UiComesAfter annotation (among other ways). For example, the following inspection result...

<entity type="Address Screen">
	<property name="city" comes-after="street">
	<action name="save" comes-after="state">
	<property name="state" comes-after="city">
	<property name="street">
</entity>

...would be sorted into...

<entity type="Address Screen">
	<property name="street">
	<property name="city" comes-after="street">
	<property name="state" comes-after="city">
	<action name="save" comes-after="state">
</entity>

The comes-after attribute can contain multiple names (comma separated) in which case the property will be sorted to come after all the named properties. Alternatively the attribute can be empty, in which case the property will come after every other property in the entity.

5.2 FacesInspectionResultProcessor

FacesInspectionResultProcessor processes the inspection result and evaluates any expressions of the form #{...} using JSF EL. For example:

<entity type="com.myapp.Person">
	<property name="pension" hidden="#{!person.retired}"/>
</entity>

FacesInspectionResultProcessor would update this inspection result's hidden attribute based on evaluating the JSF expression #{!person.retired}. It could be used to show/hide the pension field in response to the retired checkbox being checked.

Arrays and collections are also supported:

<entity type="com.myapp.Person">
	<property name="pension" lookup="#{person.arrayOfValues}"/>
</entity>

The JSF expression language also supports branching statements. For example:

import org.metawidget.inspector.annotation.*;

public class ContactBean {

	@UiAction
	@UiLabel( "#{contact.readOnly ? 'Back' : null}" )
	public void cancel() { ... }
}

FacesInspectionResultProcessor would update the label of this action to be either 'Back' or default to 'Cancel', depending on whether the Contact was being edited. It is taken from the JSF Address Book sample (see Section 1.3.2, “Web Address Book”).

If FacesInspectionResultProcessorConfig.setInjectThis is set, a special request-level _this attribute (the underscore is needed because this is a reserved word in EL) is injected into the FacesContext. JSF EL expressions rely on the JSF context being properly initialized with certain managed bean names. This is rather brittle. Instead, injecting _this allows the EL to refer to the originating object (i.e. #{_this.name}) regardless of how the JSF context is configured.

However injectThis cannot be used within attributes such as faces-lookup. Those attributes map to well-defined places within the JSF framework (i.e. f:selectItems) and are evaluated at a different phase of the JSF lifecycle. In some cases they will skip invoking FacesInspectionResultProcessor. For example if a h:selectOneMenu fails to validate during POSTback, its f:selectItems will be redisplayed without a new inspection and with no chance to injectThis.

5.3 JexlInspectionResultProcessor

JexlInspectionResultProcessor processes the inspection result and evaluates any expressions of the form ${...} using Apache Commons JEXL. It can be used to introduce declarative UI scripting into environments that lack their own expression language (i.e. JSP has an EL, Swing does not). For example:

<entity type="com.myapp.Person">
	<property name="pension" hidden="${!this.retired}"/>
</entity>

Arrays and collections are also supported:

<entity type="com.myapp.Person">
	<property name="pension" lookup="${this.arrayOfValues}"/>
</entity>

JexlInspectionResultProcessor would update this inspection result's hidden attribute based on evaluating the JEXL expression ${!this.retired} (where this refers to the instance of the Person being inspected). It could be used to show/hide the pension field in response to the retired checkbox being checked.

The JEXL expression language also supports branching statements. For example:

import org.metawidget.inspector.annotation.*;

public class ContactDialog {

	@UiAction
	@UiLabel( "${if ( this.readOnly ) 'Back'}" )
	public void cancel() { ... }
}

JexlInspectionResultProcessor would update the label of this action to be either 'Back' or default to 'Cancel', depending on whether the Contact was being edited. It is taken from the Swing Address Book sample (see Section 1.3.1, “Desktop Address Book”).

You can inject arbitary objects into the JEXL context either by subclassing and overriding JexlInspectionResultProcessor.createContext, or by using...

config.setInject( new PersonController() )

...or the equivalent via metawidget.xml...

<inject>
	<array>
		<instanceOf>com.myapp.PersonController</instanceOf>
	</array>
</inject>

Whereupon you can access the injected object through a JEXL expression such as:

${personController.all}

5.4 JspInspectionResultProcessor

JspInspectionResultProcessor processes the inspection result and evaluates any expressions of the form ${...} using JSP EL. For example:

<entity type="com.myapp.Person">
	<property name="pension" hidden="${!person.retired}"/>
</entity>

JspInspectionResultProcessor would update this inspection result's hidden attribute based on evaluating the JSP expression ${!person.retired}. It could be used to show/hide the pension field in response to the retired checkbox being checked.

Arrays and collections are also supported:

<entity type="com.myapp.Person">
	<property name="pension" lookup="${person.arrayOfValues}"/>
</entity>

The JSP expression language also supports branching statements. For example:

import org.metawidget.inspector.annotation.*;

public class ContactController {

	@UiAction
	@UiLabel( "${contactController.readOnly ? 'Back' : null}" )
	public String cancel() { ... }
}

JspInspectionResultProcessor would update the label of this action to be either 'Back' or default to 'Cancel', depending on whether the Contact was being edited. It is taken from the JSP Address Book sample (see Section 1.3.2, “Web Address Book”).

5.5 RestInspectionResultProcessor

When using one of the pure JavaScript Metawidgets (metawidget.js, metawidget-angular.js or metawidget-jqueryui.js), you can use an InspectionResultProcessor to retrieve metadata using Representational State Transfer (REST). Note this is distinct from simply retrieving data using REST. Retreiving data is usually much simpler, as it is done outside of Metawidget.

REST is best performed asychronously. Because of this, we need a way to temporarily suspend Metawidget's inspection process then resume it once the REST call has returned. Inspectors are not suitable for this, because they are designed to operate without any dependency on the UI. This is so they can be deployed on remote tiers of an application (such as the database tier). InspectionResultProcessors, on the other hand, are for those boundary cases where you need both an inspection and access to the UI. REST is one such boundary case.

To create a RestInspectionResultProcessor for your pure JavaScript application, use the following approach:

var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
	
	inspectionResultProcessors: [ function( inspectionResult, mw, toInspect, type, names ) {

		var xhr = new XMLHttpRequest();
		xhr.open( "GET", "rest/metadata/get", true );
		
		xhr.onreadystatechange = function () {
		    if ( xhr.readyState == 4 && xhr.status == 200 ) {
		    	
		    	metawidget.util.combineInspectionResults( inspectionResult,
		    		JSON.parse( xhr.responseText ));
		    	
		    	// Resume Metawidget operation
		    	
		    	mw.buildWidgets( inspectionResult );
		    }
		}
		
		xhr.send();
		
		// Return nothing to suspend Metawidget operation
	} ]
} );

For JQuery UI, the code is slightly easier:

$( "#metawidget" ).metawidget( {

	inspectionResultProcessors: [ function( inspectionResult, mw, toInspect, type, names ) {

		$.ajax( 'rest/metadata/get' ).done( function ( data ) {

			metawidget.util.combineInspectionResults( inspectionResult, JSON.parse( data ));

			// Resume Metawidget operation
			
			mw._refresh( inspectionResult );
		} );

		// Return nothing to suspend Metawidget operation
	} ]
} );

For Angular JS, the code is very similar:

$scope.metawidgetConfig = {

	inspectionResultProcessors: [ function( inspectionResult, mw, toInspect, type, names ) {

		$http.get( 'rest/metadata/get' ).then( function( result ) {

			metawidget.util.combineInspectionResults( inspectionResult, result.data );

			// Resume Metawidget operation
			
			mw.buildWidgets( inspectionResult );
		} );
		
		// Return nothing to suspend Metawidget operation
	} ]
};

Working examples of all three approaches can be found in the Metawidget integration tests.

5.6 JavaToJavaScriptTypeMappingProcessor

TypeMappingInspectionResultProcessor to map types from Java types to JavaScript types.

For Maven users, this module can be included using the classes classifier (e.g. org.metawidget.modules.js:metawidget-corejs:classes).

5.7 TypeMappingInspectionResultProcessor

InspectionResultProcessor to map types from one domain into another. Can be configured to map, say, type="string" to type="java.lang.String". Consider using one of its pre-configured subclasses for common use cases.

5.8 XmlSchemaToJavaTypeMappingProcessor

TypeMappingInspectionResultProcessor to map types from XML Schemas to Java types.

6. WidgetBuilders

This chapter covers each WidgetBuilder in detail. For an explanation of how WidgetBuilders fit into the overall architecture of Metawidget, see Chapter 2, Architecture

Throughout this chapter when we refer to 'Metawidget Attributes' we mean the intermediate XML that Metawidget passes between Inspectors and other parts of the architecture. Quite which Inspector set the attribute, and based on what, is covered in Chapter 4, Inspectors. For example, the maximum-value attribute could be set by HibernateValidatorInspector based on the @Max annotation, or by CommonsValidatorInspector based on an intRange element in an XML file, or some other source.

6.1 Desktop WidgetBuilders

Metawidget supports multiple WidgetBuilders for each desktop framework, targeting specific widget libraries within each environment.

6.1.1 Swing WidgetBuilders

OverriddenWidgetBuilder

OverriddenWidgetBuilder locates child widgets designed to override default generation. It matches JComponents whose getName equals the same name Metawidget would have generated.

ReadOnlyWidgetBuilder

ReadOnlyWidgetBuilder returns read-only widgets when either the business property is read-only or the Metawidget has setReadOnly. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
javax.swing
JLabel read-only (except when also type="java.util.Collection" or masked)
JPanel read-only and masked
org.metawidget.swing
Stub action when also read-only, type="java.util.Collection" when also read-only

SwingWidgetBuilder

SwingWidgetBuilder is the default WidgetBuilder for SwingMetawidget. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
javax.swing
JButton action (except when also read-only)
JCheckBox type="boolean" (and type="java.lang.Boolean" when also required)
JComboBox lookup
JPasswordField masked (except when also read-only)
JSlider type is a primitive (except boolean and char) and has both minimum-value and maximum-value
JSpinner type is a primitive (except boolean and char) and has only one, or neither minimum-value and maximum-value. For floats and doubles, the step size is governed by maximum-fractional-digits
JTable type="java.util.Collection". The JTable is automatically wrapped in a JScrollPane
JTextArea type="java.lang.String" and large. The JTextArea is automatically wrapped in a JScrollPane
JTextField type is a java.lang.String, java.util.Date, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand
org.metawidget.swing
Stub hidden

SwingXWidgetBuilder

SwingXWidgetBuilder is a pluggable WidgetBuilder for the SwingX library. It is intended to be used in conjunction with the default SwingWidgetBuilder. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.jdesktop.swingx
JXDatePicker type="java.util.Date"

6.1.2 SWT WidgetBuilders

OverriddenWidgetBuilder

OverriddenWidgetBuilder locates child widgets designed to override default generation. It matches Controls whose getData("name") equals the same name Metawidget would have generated.

ReadOnlyWidgetBuilder

ReadOnlyWidgetBuilder returns read-only widgets when either the business property is read-only or the Metawidget has setReadOnly. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.eclipse.swt.widgets
Label read-only (except when also type="java.util.Collection" or masked)
Composite read-only and masked
org.metawidget.swt
Stub action when also read-only, type="java.util.Collection" when also read-only

SwtWidgetBuilder

SwtWidgetBuilder is the default WidgetBuilder for SwtMetawidget. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.eclipse.swt.widgets
Button action (except when also read-only)
Button( SWT.CHECK ) type="boolean" (and type="java.lang.Boolean" when also required)
Combo lookup
Text( SWT.MASKED ) masked (except when also read-only)
Scale type is a primitive (except boolean and char) and has both minimum-value and maximum-value
Spinner type is a primitive (except boolean and char) and has only one, or neither minimum-value and maximum-value. For floats and doubles, the increment is governed by maximum-fractional-digits
Text( SWT.MULTI ) type="java.lang.String" and large
Text type is a java.lang.String, java.util.Date, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand
org.metawidget.swt
Stub hidden

6.2 Web WidgetBuilders

Metawidget supports multiple WidgetBuilders for each Web framework, targeting specific widget libraries within each environment.

6.2.1 GWT WidgetBuilders

ExtGwtWidgetBuilder

ExtGwtWidgetBuilder is a pluggable WidgetBuilder for the ExtGWT library. It is intended to be used in conjunction with the default GwtWidgetBuilder. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
com.extjs.gxt.ui.client.widget.form
DateField type="java.util.Date"
Slider type is a primitive (except boolean and char) and has both minimum-value and maximum-value

GwtWidgetBuilder

GwtWidgetBuilder is the default WidgetBuilder for GwtMetawidget. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
com.google.gwt.user.client.ui
Button action (except when also read-only)
CheckBox type="boolean" (and type="java.lang.Boolean" when also required)
ListBox lookup
PasswordTextBox masked (except when also read-only)
TextArea type="java.lang.String" and large
TextBox type of java.util.Date, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand. If maximum-length, calls setMaxlength
org.metawidget.gwt.client.ui
Stub hidden

OverriddenWidgetBuilder

OverriddenWidgetBuilder locates child widgets designed to override default generation. It matches widgets that implement HasName and whose getName equals the same name Metawidget would have generated.

ReadOnlyWidgetBuilder

ReadOnlyWidgetBuilder returns read-only widgets when either the business property is read-only or the Metawidget has setReadOnly. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
com.google.gwt.user.client.ui
Label read-only (except when also type="java.util.Collection" or masked)
SimplePanel read-only and masked
org.metawidget.gwt.client.ui
Stub action when also read-only, type="java.util.Collection" when also read-only

6.2.2 JavaScript WidgetBuilders

HtmlWidgetBuilder

HtmlWidgetBuilder is the default WidgetBuilder for metawidget.js. It returns the following DOM nodes for the following Metawidget attributes:

.
DOM Node Metawidget Attribute
<input type="button"> type: "function"
<input type="submit"> type: "function" and submit: true
<input type="checkbox"> type: "boolean"
<input type="date"> type: "date"
<input type="number"> type: "number" or type: "integer"
<input type="password"> masked (except when also read-only)
<input type="range"> type: "number" and both minimum and maximum are set
<input type="text"> type: "string". Also if property is of unknown type but dontExpand. If maxLength, adds maxlength="..."
<select> enum
<stub> hidden
<table> type: "array". Child properties will be inspected to determine table columns. Also supports columnWidth metadata
<textarea> type: "string" and large

OverriddenWidgetBuilder

OverriddenWidgetBuilder locates child widgets designed to override default generation. It matches based on the id of the DOM node. It does not match on name, because name is not a legal attribute for many nodes (e.g. table).

ReadOnlyWidgetBuilder

ReadOnlyWidgetBuilder returns read-only widgets when either the business property is read-only or the Metawidget has setReadOnly. It instantiates the following DOM nodes for the following Metawidget attributes:

DOM Node Metawidget Attribute
output read-only and enum, type: "string", type: "boolean", type: "number" or type: "date". Also masked (but the populated data will be filled with '*' characters).
stub read-only and hidden or type: "function". Also if property is of unknown type but dontExpand.

6.2.3 JSP WidgetBuilders

DisplayTagWidgetBuilder

DisplayTagWidgetBuilder is a pluggable WidgetBuilder for the DisplayTag library. It is intended to be used in conjunction with the JSP HtmlWidgetBuilder, SpringWidgetBuilder or StrutsWidgetBuilder. It instantiates the following widgets for the following Metawidget attributes:

Widget Type of field
org.displaytag.tags
TableTag type="java.util.Collection" or an array, except when also hidden or lookup. The columns in the table are based on inspecting parameterized-type or the component type of the array. If neither can be determined, the table will only have a single column

HtmlWidgetBuilder

HtmlWidgetBuilder is the default WidgetBuilder for the JSP HtmlMetawidgetTag. Since JSP has only a light component model (i.e. HTML tags such as <input> and <select> are just strings, not modelled as JSP tags), HtmlWidgetBuilder uses org.metawidget.jsp.tagext.LiteralTag to wrap string-based fragments. It returns the following literals for the following Metawidget attributes:

Widget Metawidget Attribute
<input type="checkbox"> type="boolean" (and type="java.lang.Boolean" when also required)
<input type="password"> masked (except when also read-only)
<input type="submit"> action (except when also read-only)
<input type="text"> type is a java.lang.String, java.util.Date, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand. If specified, adds maximum-length, minimum-value, maximum-value and required
<select> lookup
<textarea> type="java.lang.String" and large
org.metawidget.jsp.tagext
StubTag hidden

OverriddenWidgetBuilder

OverriddenWidgetBuilder locates child widgets designed to override default generation. It matches StubTags whose value (path for Spring environments; property for Struts environments) equals the same value Metawidget would have generated.

ReadOnlyWidgetBuilder

ReadOnlyWidgetBuilder returns read-only widgets when either the business property is read-only or the Metawidget has setReadOnly. The base class manages 'pure' JSP, with subclasses for both Spring and Struts environments. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.metawidget.jsp.tagext
(raw text) read-only (except when also type="java.util.Collection" or masked)
LiteralTag read-only and masked
StubTag action when also read-only, type="java.util.Collection" when also read-only

SpringWidgetBuilder

SpringWidgetBuilder is the default WidgetBuilder for SpringMetawidgetTag. It returns the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.springframework.web.servlet.tags.form
CheckboxTag type="boolean" (and type="java.lang.Boolean" when also required)
InputTag type is a java.lang.String, java.util.Date, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand. If maximum-length, calls setMaxlength
PasswordInputTag masked (except when also read-only)
SelectTag lookup
TextareaTag type="java.lang.String" and large
org.metawidget.jsp.tagext
StubTag action when not read-only

StrutsWidgetBuilder

StrutsWidgetBuilder is the default WidgetBuilder for StrutsMetawidgetTag. It returns the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.apache.struts.taglib.html
CheckboxTag type="boolean" (and type="java.lang.Boolean" when also required)
SelectTag lookup
PasswordTag masked (except when also read-only)
TextTag type of java.util.Date, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand. If maximum-length, calls setMaxlength
TextareaTag type="java.lang.String" and large
org.metawidget.jsp.tagext
StubTag action when not read-only

6.2.4 JSF WidgetBuilders

HtmlWidgetBuilder

HtmlWidgetBuilder is the default WidgetBuilder for the JSF HtmlMetawidget. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
javax.faces.component.html
HtmlCommandButton action (except when also read-only)
HtmlDataTable type of List, DataModel or array, except when also hidden or lookup. The columns in the table are based on inspecting parameterized-type or the component type of the array (if neither can be determined, the table will only have a single column). An additional dataTableRowAction facet can be used to add an 'action' column (for viewing or editing) - see Section 1.4.5, “ICEfaces Example”
HtmlInputSecret masked (except when also read-only)
HtmlInputText type of java.util.Date, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand. If maximum-length, calls setMaxlength
HtmlInputTextarea type="java.lang.String" and large
HtmlSelectBooleanCheckbox type="boolean" (and type="java.lang.Boolean" when also required)
HtmlSelectManyCheckbox type of List or array, with lookup
HtmlSelectOneMenu lookup
org.metawidget.faces.component
UIStub hidden

IceFacesWidgetBuilder

IceFacesWidgetBuilder is a pluggable WidgetBuilder for the JSF UIMetawidget. It is intended to be used in conjunction with the default JSF HtmlWidgetBuilder. For an example, see Section 1.4.5, “ICEfaces Example”. IceFacesWidgetBuilder instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
com.icesoft.faces.component.ext
HtmlCommandButton action (except when also read-only)
HtmlInputSecret masked (except when also read-only)
HtmlInputText type of java.util.Date, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand. If maximum-length, calls setMaxlength
HtmlInputTextarea type="java.lang.String" and large
HtmlSelectBooleanCheckbox type="boolean" (and type="java.lang.Boolean" when also required)
HtmlSelectManyCheckbox type="boolean" (and type="java.lang.Boolean" when also required)
HtmlSelectOneMenu lookup
com.icesoft.faces.component
SelectInputDate type="java.util.Date"

Important: see the section called “Special considerations for Java Server Faces”

By default, all components are instantiated with partialSubmit set to true. This can be configured through IceFacesWidgetBuilderConfig.

OverriddenWidgetBuilder

OverriddenWidgetBuilder locates child widgets designed to override default generation. It matches UIComponents whose value binding or action binding equals the same binding Metawidget would have generated.

ReadOnlyWidgetBuilder

ReadOnlyWidgetBuilder returns read-only widgets when either the business property is read-only or the Metawidget has setReadOnly. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
javax.faces.component.html
HtmlOutputText read-only (except when also type="java.util.Collection" or masked)
org.metawidget.faces.component
HtmlLookupOutputText read-only with lookup-label
UIStub action when also read-only, masked when also read-only, type="java.util.Collection" when also read-only

PrimeFacesWidgetBuilder

PrimeFacesWidgetBuilder is a pluggable WidgetBuilder for the JSF UIMetawidget. It is intended to be used in conjunction with the default JSF HtmlWidgetBuilder. PrimeFacesWidgetBuilder instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.primefaces.component
AutoComplete faces-suggest
Calendar type="java.util.Date"
ColorPicker type="java.awt.Color"
Stub containing an InputText and a Slider type is a primitive (except boolean and char) and has both minimum-value and maximum-value
Spinner type is a primitive (except boolean and char) or a Number and has only one, or neither minimum-value and maximum-value

RichFacesWidgetBuilder

RichFacesWidgetBuilder is a pluggable WidgetBuilder for the JSF UIMetawidget. It is intended to be used in conjunction with the default JSF HtmlWidgetBuilder. For an example, see the section called “Alternate Widget Libraries (JSF 1.x)”. RichFacesWidgetBuilder instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.richfaces.component.html
HtmlCalendar type="java.util.Date"
HtmlColorPicker type="java.awt.Color"
HtmlInputNumberSlider type is a primitive (except boolean and char) and has both minimum-value and maximum-value
HtmlInputNumberSpinner type is a primitive (except boolean and char) or a Number and has only one, or neither minimum-value and maximum-value
HtmlSuggestionBox faces-suggest

TomahawkWidgetBuilder

TomahawkWidgetBuilder is a pluggable WidgetBuilder for the JSF UIMetawidget. It is intended to be used in conjunction with the default JSF HtmlWidgetBuilder. TomahawkWidgetBuilder instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
org.apache.myfaces.custom
HtmlInputFileUpload type="org.apache.myfaces.custom.fileupload.UploadedFile"

6.2.5 Vaadin WidgetBuilders

OverriddenWidgetBuilder

OverriddenWidgetBuilder locates child widgets designed to override default generation. It matches Components whose getData equals the name of the business property.

ReadOnlyWidgetBuilder

ReadOnlyWidgetBuilder returns read-only widgets when either the business property is read-only or the Metawidget has setReadOnly. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
com.vaadin.ui
Button (disabled) read-only and action
Label read-only (except when action, masked or large)
Panel read-only and masked
TextArea (disabled) read-only and large
org.metawidget.vaadin.ui.widgetbuilder
LookupLabel read-only with lookup-label

VaadinWidgetBuilder

VaadinWidgetBuilder is the default WidgetBuilder for VaadinMetawidget. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
com.vaadin.ui
Button action (except when also read-only)
CheckBox type="boolean" (and type="java.lang.Boolean" when also required)
ComboBox lookup
PasswordField masked (except when also read-only)
PopupDateField type of java.util.Date
Slider type is a primitive (except boolean and char) and has both minimum-value and maximum-value
TextArea type="java.lang.String" and large
TextField type of java.lang.String, primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand. If maximum-length, calls setMaxlength
org.metawidget.vaadin.ui
Stub hidden

6.3 Mobile WidgetBuilders

Metawidget supports multiple WidgetBuilders for each mobile framework, targeting specific widget libraries within each environment.

6.3.1 Android WidgetBuilders

AndroidWidgetBuilder

AndroidWidgetBuilder is the default WidgetBuilder for AndroidMetawidget. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
android.widget
CheckBox type="boolean" (and type="java.lang.Boolean" when also required)
DatePicker type="java.utils.Date" when also required
EditText type of primitive wrapper (e.g. Integer or Float) except java.lang.Boolean. Also if property is of unknown type but dont-expand. If large, calls setMinLines. If masked, sets a PasswordTransformationMethod. If numeric, sets a DigitsKeyListener. If maximum-length, sets a InputFilter.LengthFilter. If type is a Date, sets a DateKeyListener
PasswordTextBox masked (except when also read-only)
Spinner lookup
org.metawidget.gwt.client.ui
Stub hidden

OverriddenWidgetBuilder

OverriddenWidgetBuilder locates child widgets designed to override default generation. It matches Views whose tag equals the same tag Metawidget would have generated.

ReadOnlyWidgetBuilder

ReadOnlyWidgetBuilder returns read-only widgets when either the business property is read-only or the Metawidget has setReadOnly. It instantiates the following widgets for the following Metawidget attributes:

Widget Metawidget Attribute
android.widget
TextView read-only (except when also type="java.util.Collection"). If masked sets View.INVISIBLE
org.metawidget.gwt.client.ui
Stub action when also read-only, masked when also read-only, type="java.util.Collection" when also read-only

7. WidgetProcessors

This chapter covers each WidgetProcessor in detail. For an explanation of how WidgetProcessors fit into the overall architecture of Metawidget, see Chapter 2, Architecture.

7.1 Desktop WidgetProcessors

Metawidget supports multiple WidgetProcessors for each desktop framework, targeting specific features within each environment.

7.1.1 Swing WidgetProcessors

Property Binding

Swing does not include an automatic JComponent to Object binding mechanism, but Metawidget supports third-party alternatives via addWidgetProcessor.

BeansBindingProcessor

BeansBindingProcessor binds properties using Beans Binding (JSR-295). It supports the various Beans Binding update strategies:

myMetawidget.addWidgetProcessor( new BeansBindingProcessor(
	new BeansBindingProcessorConfig()
		.setUpdateStrategy( UpdateStrategy.READ )) );

If set to READ or READ_WRITE (the default is READ_ONCE), the object being inspected must provide PropertyChangeSupport. If set to READ_WRITE, updates to the UI are automatically sync'ed back to the setToInspect, otherwise the client must manually call save:

myMetawidget.getWidgetProcessor( BeansBindingProcessor.class ).save( myMetawidget )

After JComponents have been generated for the initial setToInspect, clients can load values for a new Object into the same UI without a full re-inspection by using rebind:

myMetawidget.getWidgetProcessor( BeansBindingProcessor.class ).rebind( newObject, myMetawidget )

For more details, see Section 9.5.4, “Rebinding”.

[Important]Make sure you use the correct version of BeansBinding
There are unfortunately two versions of BeansBinding 1.2.1 available. The version at http://repo2.maven.org/maven2/net/java/dev/beansbinding/beansbinding/1.2.1 has serious bugs related to PropertyChangeListeners. The version at http://download.java.net/maven/2/org/jdesktop/beansbinding/1.2.1 is the correct one. This issue has been reported here.
[Tip]Consider BetterBeansBinding
BeansBinding 1.2.1 has known performance issues when working with large numbers of widgets. Consider a more recent JSR-295 implementation such as BetterBeansBinding.
BeanUtilsProcessor

BeanUtilsProcessor binds properties using Apache BeanUtils. It supports arbitrary property styles:

myMetawidget.addWidgetProcessor( new BeanUtilsBindingProcessor(
	new BeanUtilsBindingProcessorConfig()
		.setPropertyStyle( new ScalaPropertyStyle() ) );

Updates to the UI can be saved back to the setToInspect by calling save:

myMetawidget.getWidgetProcessor( BeanUtilsBindingProcessor.class ).save( myMetawidget )

After JComponents have been generated for the initial setToInspect, clients can load values for a new Object into the same UI without a full re-inspection by using rebind:

myMetawidget.getWidgetProcessor( BeanUtilsBindingProcessor.class ).rebind( newObject, myMetawidget )

For more details, see Section 9.5.4, “Rebinding”.

Action Binding

Swing supplies javax.swing.Action for binding JButtons to backing objects, and this is typically combined with Java-based reflection to support runtime binding. This is exactly what the default action binding, ReflectionBinding, does.

However, Metawidget makes action bindings pluggable to support other use cases. In particular, use cases where there is no backing object, and instead the JButton should invoke, say, an RPC call. Implement your own pluggable binding by implementing WidgetProcessor:

public class RpcWidgetProcessor
	implements WidgetBuilder<JComponent, SwingMetawidget> {
										
	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 e ) {
			
				String actionName = attributes.get( NAME );
				...make RPC using actionName...
			}
		}
	}
}

This can be set either in code:

myMetawidget.addWidgetProcessor( new RpcWidgetProcessor() );

Or via metawidget.xml:

<swingMetawidget xmlns="java:org.metawidget.swing">
	<widgetProcessors>
		<array>
			<rpcWidgetProcessor xmlns="java:com.myapp"/>
		</array>
	</widgetProcessors>
</swingMetawidget>

7.1.2 SWT WidgetProcessors

DataBindingProcessor

DataBindingProcessor binds properties using org.eclipse.core.databinding. Once bound, values can be saved back from the UI by calling save:

myMetawidget.getWidgetProcessor( DataBindingProcessor.class ).save( myMetawidget )

ReflectionBindingProcessor

ReflectionBindingProcessor binds Buttons to backing objects using Java-based reflection to support runtime binding.

After JComponents have been generated for the initial setToInspect, clients can load values for a new Object into the same UI without a full re-inspection by using rebind:

myMetawidget.getWidgetProcessor( ReflectionBindingProcessor.class ).rebind( newObject, myMetawidget )

For more details, see Section 9.5.4, “Rebinding”.

7.2 Web WidgetProcessors

Metawidget supports multiple WidgetProcessors for each Web framework, targeting specific features within each environment.

7.2.1 GWT WidgetProcessors

Property Binding

Like most other Metawidgets, GwtMetawidget supports property binding. Property binding generally requires reflection, and GWT recommends using Generators to achieve this. As of the time of writing, however, much of the burden of implementation rests on the developer.

GwtMetawidget automates this burden by supplying a SimpleBindingProcessor implementation. This implementation is pluggable, so may be swapped out as and when later releases of GWT more fully support data binding.

SimpleBindingProcessor expects every domain object to be wrapped with a SimpleBindingProcessorAdapter. The supplied SimpleBindingProcessorAdapterGenerator automates this process. To configure it, add the following to the application-name.gwt.xml file...

<generate-with
	class="org.metawidget.gwt.generator.widgetprocessor.binding.simple.SimpleBindingProcessorAdapterGenerator">
	<when-type-assignable class="org.metawidget.example.shared.addressbook.model.Contact"/>
</generate-with>

...and in the application code...

metawidget.addWidgetProcessor( new SimpleBindingProcessor(
new SimpleBindingProcessorConfig().setAdapter( Contact.class,
	(SimpleBindingProcessorAdapter<Contact>) GWT.create(Contact.class)));
[Important]ClassCastException
If this line throws a ClassCastException casting to SimpleBindingProcessorAdapter, it means GWT is not applying the SimpleBindingProcessorAdapterGenerator. Check you've specified the correct class in your generate-with block.

Updates to the UI can be saved back to the setToInspect by calling save:

myMetawidget.getWidgetProcessor( SimpleBindingProcessor.class ).save( myMetawidget )

After Widgets have been generated for the initial setToInspect, clients can load values for a new Object into the same UI without a full re-inspection by using rebind:

myMetawidget.getWidgetProcessor( SimpleBindingProcessor.class ).rebind( newObject, myMetawidget )

For more details, see Section 9.5.4, “Rebinding”.

Action Binding

GWT supplies com.google.gwt.user.client.ui.ClickListener for binding Buttons to backing objects. It is futher possible to combine this with a generator to support runtime binding. This is exactly what the default action binding, SimpleBindingProcessor, does.

However, Metawidget makes action bindings pluggable to support other use cases. In particular, use cases where there is no backing object, and instead the Button should invoke, say, an RPC call (for an example, see Section 1.4.9, “GWT Client Side Example”). Implement your own pluggable binding by implementing WidgetProcessor and use it by calling...

myMetawidget.addWidgetProcessor( new MyWidgetProcessor() );

StyleNameProcessor

StyleNameProcessor sets styleName on all generated widgets, based on the styleName of the parent Metawidget. It does not 'drill into' Stubs.

7.2.2 JavaScript WidgetProcessors

AngularWidgetProcessor

WidgetProcessor to add Angular JS bindings and validation, and compile the widget.

BootstrapWidgetProcessor

WidgetProcessor to add CSS styles for Bootstrap. For example, adds:

HTML Tag Name CSS
BUTTON btn
TABLE table table-striped table-bordered table-hover
INPUT SPAN class="add-on" if inputPrepend attribute is set

DisabledAttributeProcessor

WidgetProcessor that sets the 'disabled' attribute.

IdProcessor

WidgetProcessor that sets the 'id' attribute.

RequiredAttributeProcessor

WidgetProcessor that sets the 'required' attribute.

SimpleBindingProcessor

Simple data/action binding implementation. Frameworks that supply their own data-binding mechanisms should override this with their own WidgetProcessor.

SimpleBindingProcessor binds properties using widget.value. To save values back into the domain object, clients must call save:

mw.getWidgetProcessor( function( widgetProcessor ) {
	return widgetProcessor instanceof metawidget.widgetprocessor.SimpleBindingProcessor;
} ).save( mw );

SimpleBindingProcessor can also reload the values in the widgets. This is independent of toInspect, and it doesn't save any values back from the widgets. But it can be useful in Node.js environments for re-populating the widgets based on an HTTP request POST-back:

mw.getWidgetProcessor( function( widgetProcessor ) {
	return widgetProcessor instanceof metawidget.widgetprocessor.SimpleBindingProcessor;
} ).reload( requestData, mw );

7.2.3 JSF WidgetProcessors

AjaxProcessor

AjaxProcessor adds a JSF 2.0 AjaxBehavior to any widgets with a faces-ajax-action attribute.

CssStyleProcessor

CssStyleProcessor sets style and styleClass properties on all generated widgets, based on the style and styleClass of the parent Metawidget. It does not 'drill into' UIStubs.

HiddenFieldProcessor

Many Web applications store their data at the HttpServletRequest level, not at the HttpSession level. Using session-level state (or, ideally, a UI framework that supports some kind of 'conversation'-level state) is safer than passing variables to and from the client in hidden HTML fields. However, for those that need this approach HiddenFieldProcessor wraps a Stub containing an HtmlInputHidden around read-only values, so that they POST back.

ImmediateAttributeProcessor

ImmediateAttributeProcessor sets UIInput.setImmediate for any widgets with a faces-immediate attribute.

LabelProcessor

LabelProcessor sets JSF 1.2's setLabel for those UIComponents that support it. This label is used during validation errors and conversion errors (even if the UIComponent does not explicitly have a converter).

ReadableIdProcessor

ReadableIdProcessor sets 'human readable' ids for each widget. Unlike UIViewRoot.createUniqueId, it tries to make the id readable both for debugging purposes and for when running unit tests (using, say, WebTest). Because the ids are based off the value binding (or method binding) of the UIComponent, this WidgetProcessor must come after StandardBindingProcessor (or equivalent).

Clients can plug in a different WidgetProcessor to use UIViewRoot.createUniqueId if preferred. They can even plug in assigning a changing, random id to a component each time it is generated. This is a great way to fox hackers who are trying to POST back pre-generated payloads of HTTP fields (i.e. CSRF attacks).

RequiredAttributeProcessor

RequiredAttributeProcessor sets UIInput.setRequired for any widgets with a required attribute.

RichFacesProcessor

RichFacesProcessor adds a RichFaces AJAX Support widget to any widgets with a faces-ajax-action attribute (for JSF 2.0, see also AjaxProcessor).

StandardBindingProcessor

StandardBindingProcessor attaches standard JSF value and action bindings to each widget.

StandardConverterProcessor

StandardConverterProcessor attaches standard JSF converters to each widget as needed. This includes DateTimeConverter and NumberConverter, and support for the faces-converter attribute.

StandardValidatorProcessor

StandardValidatorProcessor attaches standard JSF validators to each widget as needed. This includes DoubleRangeValidator, LongRangeValidator and LengthValidator.

7.2.4 JSP WidgetProcessors

HiddenFieldProcessor

Many Web applications store their data at the HttpServletRequest level, not at the HttpSession level. Using session-level state (or, ideally, a UI framework that supports some kind of 'conversation'-level state) is safer than passing variables to and from the client in hidden HTML fields. However, for those that need this approach HiddenFieldProcessor adds HTML hidden fields to read-only values, so that they POST back.

There are subclasses of HiddenFieldProcessor for JSP, Spring and Struts.

7.2.5 Vaadin WidgetProcessors

CaptionProcessor

CaptionProcessor calls setCaption on a Component, based on an uncamel-cased version of the business property name. This can then be used by Layouts such as org.metawidget.vaadin.ui.layout.FormLayout.

MinimumMaximumValidatorProcessor

MinimumMaximumValidatorProcessor adds a com.vaadin.data.Validator to a Component, based on either (or both) the Metawidget attributes minimum-value or maximum-value being set. These attributes can be set by, for example, using Hibernate Validator's @Range annotation.

RequiredProcessor

RequiredProcessor calls setRequired and setRequiredError on a Component, based on the Metawidget required attribute being set. This attribute can be set by, for example, using a @UiRequired annotation or Bean Validation's @NotNull annotation.

SimpleBindingProcessor

Like most other Metawidgets, VaadinMetawidget supports property binding. As of the time of writing, however, Vaadin leaves much of the implementation of property binding to the developer. VaadinMetawidget automates this by supplying a SimpleBindingProcessor. This implementation is pluggable, so may be swapped out as and when later releases of Vaadin more fully support data binding.

SimpleBindingProcessor binds properties using setValue. To save values back into the domain object, clients must call save:

myMetawidget.getWidgetProcessor( SimpleBindingProcessor.class ).save( myMetawidget )

If the type of the business property does not match the type expected by the Component, clients can register a Converter. First implement the org.metawidget.vaadin.ui.widgetprocessor.binding.simple.Converter interface...

public class ColorToStringConverter
	implements Converter<Color, String> {

	public String convert( Color value, Class<? extends String> expectedType ) {

		// return Color as a String (e.g. ff0000)
	}
}

...then register it either programmatically or in metawidget.xml:

<widgetProcessors>
	<array>
		<simpleBindingProcessor xmlns="java:org.metawidget.vaadin.ui.widgetprocessor.binding.simple"
				config="SimpleBindingProcessorConfig">
			<converter>
				<class>java.lang.Color</class>
				<class>java.lang.String</class>
				<colorToStringConverter xmlns="java:com.myapp"/>
			</converter>					
			<converter>
				...another converter...
			</converter>					
		</simpleBindingProcessor>
		...

7.3 Mobile WidgetProcessors

Metawidget supports WidgetProcessors for mobile environments.

7.3.1 Android WidgetProcessors

DisabledAttributeProcessor

DisabledAttributeProcessor calls setEnabled(false) on a View, based on the disabled attribute.

SimpleBindingProcessor

Like most other Metawidgets, AndroidMetawidget supports property binding. As of the time of writing, however, Android leaves much of the implementation of property binding to the developer. AndroidMetawidget automates this by supplying a SimpleBindingProcessor. This implementation is pluggable, so may be swapped out as and when later releases of Android more fully support data binding.

SimpleBindingProcessor binds properties using setValue. To save values back into the domain object, clients must call save:

myMetawidget.getWidgetProcessor( SimpleBindingProcessor.class ).save( myMetawidget )

If the type of the business property does not match the type expected by the View, clients can register a Converter. First implement the org.metawidget.android.widget.widgetprocessor.binding.simple.Converter interface...

public class ColorConverter
	implements Converter<Color> {

	public Color convertFromView( View widget, Object value, Class<?> intoClass ) {

		// return (String) value as a Color
	}
	
	Object convertForView( View widget, Color value ) {
	
		// return Color as a String (e.g. ff0000)
	}
}

...then register it either programmatically or in metawidget.xml:

<widgetProcessors>
	<array>
		<simpleBindingProcessor xmlns="java:org.metawidget.android.widget.widgetprocessor.binding.simple"
				config="SimpleBindingProcessorConfig">
			<converter>
				<class>java.lang.Color</class>
				<colorConverter xmlns="java:com.myapp"/>
			</converter>					
			<converter>
				...another converter...
			</converter>					
		</simpleBindingProcessor>
		...

8. Layouts

This chapter covers each Layout in detail. For an explanation of how Layouts fit into the overall architecture of Metawidget, see Chapter 2, Architecture.

8.1 Desktop Layouts

Metawidget supports multiple Layouts for each desktop framework, targeting different arrangements within each environment.

8.1.1 Swing Layouts

BoxLayout

Layout to simply output components one after another, with no labels and no structure, using javax.swing.BoxLayout. This is like FlowLayout (below), except it fills width. It can be useful for JTable CellEditors (see Section 1.3.1, “Desktop Address Book”).

FlowLayout

Layout to simply output components one after another, with no labels and no structure, using java.awt.FlowLayout. This is like BoxLayout, except it does not fill width. It can be useful for button bars (see Section 1.3.1, “Desktop Address Book”).

GridBagLayout

Layout to arrange widgets using java.awt.GridBagLayout. Widgets are arranged in a table, with one column for the label and another for the widget. This Layout recognizes the following parameters, configured using GridBagLayoutConfig:

Property Description
labelAlignment Such as SwingConstants.LEFT or SwingConstants.RIGHT
labelFont Label font
labelForeground Label foreground color
labelSuffix Text to display after label text. Defaults to a colon (:)
numberOfColumns Number of columns. Each label/component pair is considered one column
requiredAlignment Alignment of the asterisk relative to the component. Can be SwingConstants.LEFT, SwingConstants.CENTER or SwingConstants.RIGHT
requiredText Text to display for required properties. Defaults to an asterisk
supportMnemonics Whether to convert &'s in labels into keyboard mnemonics

GroupLayout

Layout to arrange widgets using javax.swing.GroupLayout. Widgets are arranged in a table, with one column for the label and another for the widget.

MigLayout

Layout to arrange widgets using net.miginfocom.swing.MigLayout. Widgets are arranged in a table, with one column for the label and another for the widget. This Layout recognizes the following parameters, configured using MigLayoutConfig:

Property Description
numberOfColumns Number of columns. Each label/component pair is considered one column
supportMnemonics Whether to convert &'s in labels into keyboard mnemonics

SeparatorLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a JSeparator. This LayoutDecorator recognizes the following parameters, configured using SeparatorLayoutDecoratorConfig:

Property Description
alignment One of SwingConstants.LEFT or SwingConstants.RIGHT.
layout Metawidget Layout to use for laying out the sections, for example org.metawidget.swing.layout.GridBagLayout.

TabbedPaneLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a JTabbedPane. This LayoutDecorator recognizes the following parameters, configured using TabbedPaneLayoutDecoratorConfig:

Property Description
layout Metawidget Layout to use for laying out the sections, for example org.metawidget.swing.layout.GridBagLayout.
tabPlacement One of SwingConstants.TOP, SwingConstants.BOTTOM, SwingConstants.LEFT or SwingConstants.RIGHT as defined by JTabbedPane.setTabPlacement.

TitledPanelLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a JPanel with a TitledBorder. This LayoutDecorator recognizes the following parameters, configured using LayoutDecoratorConfig:

Property Description
layout Metawidget Layout to use for laying out the sections, for example org.metawidget.swing.layout.GridBagLayout.

8.1.2 SWT Layouts

FillLayout

Layout to simply output components one after another, with no labels and no structure, using org.eclipse.swt.layout.FillLayout. This is like RowLayout, except it fills width. It can be useful for Table Editors (see Section 1.3.1, “Desktop Address Book”).

GridLayout

Layout to arrange widgets using org.eclipse.swt.layout.GridLayout. Widgets are arranged in a table, with one column for the label and another for the widget. This Layout recognizes the following parameters, configured using GridLayoutConfig:

Property Description
labelAlignment Such as SWT.LEFT or SWT.RIGHT
labelFont Label font
labelForeground Label foreground color
labelSuffix Text to display after label text. Defaults to a colon (:)
numberOfColumns Number of columns. Each label/component pair is considered one column
requiredAlignment Alignment of the asterisk relative to the control. Can be SWT.LEFT or SWT.RIGHT
requiredText Text to display for required properties. Defaults to an asterisk

MigLayout

Layout to arrange widgets using net.miginfocom.swt.MigLayout. Widgets are arranged in a table, with one column for the label and another for the widget. This Layout recognizes the following parameters, configured using MigLayoutConfig:

Property Description
numberOfColumns Number of columns. Each label/component pair is considered one column

RowLayout

Layout to simply output components one after another, with no labels and no structure, using org.eclipse.swt.layout.RowLayout. This is like FillLayout, except it does not fill width. It can be useful for button bars (see Section 1.3.1, “Desktop Address Book”).

SeparatorLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a Label( SWT.SEPARATOR ). This LayoutDecorator recognizes the following parameters, configured using SeparatorLayoutDecoratorConfig:

Property Description
alignment One of SWT.LEFT or SWT.RIGHT.
layout Metawidget Layout to use for laying out the sections, for example org.metawidget.swt.layout.GridLayout.

TabFolderLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a TabFolder. This LayoutDecorator recognizes the following parameters, configured using TabFolderLayoutDecoratorConfig:

Property Description
layout Metawidget Layout to use for laying out the sections, for example org.metawidget.swt.layout.GridLayout.
tabLocation One of SWT.TOP or SWT.BOTTOM.

8.2 Web Layouts

Metawidget supports multiple Layouts for each Web framework, targeting different arrangements within each environment.

8.2.1 GWT Layouts

FlexTableLayout

Layout to arrange widgets in a table, with one column for the label and another for the widget. This Layout recognizes the following parameters, configured using FlexTableLayoutConfig:

Property Description
columnStyleNames Comma delimited string of CSS style classes to apply to table columns in order of: label, component, required
footerStyleName CSS style class to apply to table footer
numberOfColumns Number of columns. Each label/component pair is considered one column
tableStyleName CSS style class to apply to outer table tag

FlowLayout

Layout to simply output components one after another, with no labels and no structure. This Layout is suited to rendering single components, or for rendering components whose layout relies entirely on CSS.

LabelLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a Label. This Layout recognizes the following parameters, configured using LabelLayoutDecoratorConfig:

Property Description
styleName CSS style class to apply to section heading

TabPanelLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a TabPanel.

8.2.2 JavaScript Layouts

All of the supported JavaScript-based technologies (i.e. pure JavaScript, JQuery UI and AngularJS) share the same Layouts.

BootstrapDivLayout

Layout to arrange widgets using div tags, suitable for form-vertical or form-horizontal Bootstrap layouts.

This layout extends metawidget.layout.DivLayout. It adds Bootstrap CSS classes such as form-group and control-label to the divs. This Layout recognizes the following configuration properties:

Property Description
version Specify '2' to use Bootstrap 2.x CSS class names (Bootstrap 3.x names are used by default)

DefinitionListLayout

Layout to arrange widgets using dl, dd and dt tags. This Layout recognizes the following configuration properties:

Property Description
labelStyleClass Style class to apply to the generated label.
labelSuffix Suffix to append to the generated label.

DivLayout

Layout to arrange widgets using div tags. This Layout recognizes the following configuration properties:

Property Description
divStyleClasses Comma separated list of style classes to apply to the divs, in order of outer, label, component.
labelStyleClass Style class to apply to the generated label.
labelSuffix Suffix to append to the generated label.
suppressLabelSuffixOnCheckboxes Some UI frameworks (like JQuery Mobile) reuse the checkbox/radio label alongside the checkbox/radio itself. This looks bad if we keep the suffix.
wrapInsideLabels Array of widget types (e.g. checkbox, radio) to be placed inside their labels, rather than alongside.
wrapWithExtraDiv Object of key/value pairs of widget types (e.g. checkbox, radio) to be wrapped inside an extra div. The key is the widget type, the value is the CSS class for the extra div.
suppressDivAroundLabel Some UI frameworks (like JQuery Mobile) prefer not to wrap the label with a div.
suppressDivAroundWidget Some UI frameworks (like JQuery Mobile) prefer not to wrap the widget with a div.
appendRequiredClassOnLabelDiv Style class to apply to the label div if the widget is required. Can be useful for CSS :after selectors.
appendRequiredClassOnWidgetDiv Style class to apply to the widget div if the widget is required. Can be useful for CSS :after selectors.

HeadingTagLayoutDecorator

LayoutDecorator to decorate widgets from different sections using an HTML heading tag (i.e. h1, h2 etc). This Layout recognizes the following configuration properties:

Property Description
level The first level of the heading tags (i.e. 3 for h3).

TabLayoutDecorator (Bootstrap)

LayoutDecorator to decorate widgets from different sections using Bootstrap tabs.

TabLayoutDecorator (JQuery UI)

LayoutDecorator to decorate widgets from different sections using JQuery UI tabs.

TableLayout

Layout to arrange widgets in a table, with one column for the label and another for the widget. This Layout recognizes the following configuration properties:

Property Description
columnStyleClasses Comma delimited string of CSS style classes to apply to table columns in order of: label, component, required
footerStyleClass CSS style class to apply to table footer
numberOfColumns Number of columns. Each label/component pair is considered one column
tableStyleClass CSS style class to apply to outer table tag

SimpleLayout

Layout to simply output components one after another, with no labels and no structure. This Layout is suited to rendering single components, or for rendering components whose layout relies entirely on CSS.

8.2.3 JSF Layouts

Layouts in JSF behave a little differently to those for other Metawidgets. By and large, JSF already provides a pluggable mechanism (javax.faces.render.Renderers) that can be used for displaying a component in different ways. Metawidget leverages this mechanism via its xxxLayoutRenderer classes (see below).

However, JSF Renderers are not supposed to modify the component tree (say, to wrap a group of components into a tabbed panel). For those cases, Metawidget employs its usual Layout mechanism. Layouts are executed at component-building-time, which is before Renderer-time so can safely modify the component tree.

HtmlDivLayoutRenderer

Layout to arrange widgets in HTML DIV tags, with one DIV per label and per widget, and an outer DIV around both. This Layout recognizes the following parameters (passed either as <f:param> tags or set via <parameter> in metawidget.xml):

Parameter Description
componentStyle CSS styles to apply to the component DIV. This is the style applied to the DIV around the component, not to the component itself. The widget component can be styled using the style attribute on the Metawidget tag
divStyleClasses Comma separated list of style classes to apply to the DIVs, in order of outer, label, required, component, errors
inlineMessages Whether to wrap input components with inline h:message tags. True by default
labelStyle CSS styles to apply to the label DIV
outerStyle CSS styles to apply to the outer DIV
requiredStyle CSS styles to apply to the required DIV

HtmlTableLayoutRenderer

Layout to arrange components in a table, with one column for the label and another for the component. This Layout recognizes the following parameters (passed either as <f:param> tags or set via <parameter> in metawidget.xml):

Parameter Description
columns Number of columns. Each label/component pair is considered one column
columnClasses Comma delimited string of CSS style classes to apply to table columns in order of: label, component, required
componentStyle CSS styles to apply to required column
footerStyle CSS styles to apply to table footer
footerStyleClass CSS style class to apply to table footer
headerStyle CSS styles to apply to table header
headerStyleClass CSS style class to apply to table header
inlineMessages Whether to wrap input components with inline h:message tags. True by default
labelStyle CSS styles to apply to label column
labelSuffix Suffix to put after the label name. Defaults to a colon (i.e. 'Name:')
requiredStyle CSS styles to apply to required column (i.e. the asterisk)
rowClasses Comma delimited string of CSS style classes to apply to alternating table rows
tableStyle CSS styles to apply to outer table tag
tableStyleClass CSS style class to apply to outer table tag

OutputTextLayoutDecorator

LayoutDecorator to decorate widgets from different sections using an HtmlOutputText. As dictated by the JSF spec, CSS styles and style classes applied to an HtmlOutputText are wrapped in an HTML span tag. Therefore you must use CSS...

display: block

...if you want to use margins or padding around the HtmlOutputText.

This LayoutDecorator recognizes the following parameters, configured using OutputTextLayoutDecoratorConfig:

Property Description
style CSS styles to apply to HtmlOutputText
styleClass CSS style class to apply to HtmlOutputText

PanelGroupLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a JSF PanelGroup. This LayoutDecorator recognizes the following parameters, configured using PanelGroupLayoutDecoratorConfig:

Property Description
panelLayout The type of layout markup to use when rendering this group. As per javax.faces.component.html.HtmlPanelGroup.setLayout
style CSS styles to apply to the panel
styleClass CSS style class to apply to the panel

PanelLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a RichFaces Panel. This LayoutDecorator recognizes the following parameters, configured using PanelLayoutDecoratorConfig:

Property Description
layout Layout to apply to the panel, as defined by javax.faces.component.html.HtmlPanelGroup.setLayout
style CSS styles to apply to the panel
styleClass CSS style class to apply to the panel

PanelTabSetLayoutDecorator

LayoutDecorator to decorate widgets from different sections using an ICEfaces PanelTabSet.

SimpleTogglePanelLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a RichFaces SimpleTogglePanel. This LayoutDecorator recognizes the following parameters, configured using SimpleTogglePanelLayoutDecoratorConfig:

Property Description
style CSS styles to apply to the panel
styleClass CSS style class to apply to the panel
switchType Mechanism to use to open/close panels. Either 'client' or 'ajax'. Default is 'client'
opened Whether the panel is initially opened. Defaults to true

HtmlSimpleLayout

HtmlSimpleLayout is the default JSF Layout. It simply adds the widget as a child of the Metawidget, leaving everything up to the Renderer.

TabPanelLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a RichFaces TabPanel. This LayoutDecorator recognizes the following parameters, configured using TabPanelLayoutDecoratorConfig:

Property Description
headerAlignment Defaults to 'left'

TabViewLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a PrimeFaces TabView.

8.2.4 JSP Layouts

All of the supported JSP-based technologies (i.e. 'pure' JSP, Spring and Struts) share the same Layouts.

HeadingTagLayoutDecorator

LayoutDecorator to decorate widgets from different sections using an HTML heading tag (i.e. H1, H2 etc). This LayoutDecorator recognizes the following parameters, configured using HeadingTagLayoutDecoratorConfig:

Property Description
style CSS styles to apply to heading tag
styleClass CSS style class to apply to heading tag

HtmlTableLayout

Layout to arrange widgets in a table, with one column for the label and another for the widget. This Layout recognizes the following parameters, configured using HtmlTableLayoutConfig:

Property Description
columnStyleClasses Comma delimited string of CSS style classes to apply to table columns in order of: label, component, required
footerStyle CSS styles to apply to table footer
footerStyleClass CSS style class to apply to table footer
numberOfColumns Number of columns. Each label/component pair is considered one column
tableStyle CSS styles to apply to outer table tag
tableStyleClass CSS style class to apply to outer table tag

SpringTableLayout

Extends HtmlTableLayout to add inline error validation.arrange widgets

Property Description
errorStyle CSS styles to apply to inline error
errorStyleClass CSS style class to apply to inline error

SimpleLayout

Layout to simply output components one after another, with no labels and no structure. This Layout is suited to rendering single components, or for rendering components whose layout relies entirely on CSS.

8.2.5 Vaadin Layouts

FormLayout

Layout to arrange widgets in a table, with one column for the label and another for the widget. This Layout recognizes the following parameters, configured using FormLayoutConfig:

Property Description
labelSuffix Text to display after label text. Defaults to a colon (:)

HeadingTagLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a Label with setStyleName of h1, h2 etc.

HorizontalLayout

Layout to simply output components one after another, with no labels and no structure, using com.vaadin.ui.HorizontalLayout.

TabSheetLayoutDecorator

LayoutDecorator to wrap widgets in different sections inside a TabSheet. Each tab is given the name of a section.

8.3 Mobile Layouts

Metawidget supports multiple Layouts for each mobile framework, targeting different arrangements within each environment.

8.3.1 Android Layouts

LinearLayout

Layout to arrange widgets vertically, using android.widget.LinearLayout. This Layout recognizes the following parameters, configured using LinearLayoutConfig:

Property Description
labelStyle Android style (i.e. @com.myapp:style/label) to apply to labels. The style should be defined within res/values/styles.xml. For example:
<style name="label">
	<item name="android:textSize">10sp</item>
</style>

TableLayout

Layout to arrange widgets in a table, with one column for the label and another for the widget, using android.widget.TableLayout. This Layout can be configured using LinearLayoutConfig, as described in the previous section.

TabHostLayoutDecorator

LayoutDecorator to wrap widgets in different sections inside a TabHost, as in Figure 8.1.

TabHostLayoutDecorator

Figure 8.1. TabHostLayoutDecorator


This LayoutDecorator recognizes the following parameters, configured using LayoutDecoratorConfig:

Property Description
layout Metawidget Layout to use for laying out the inside of the tabs, for example org.metawidget.android.widget.layout.TableLayout.

TextViewLayoutDecorator

LayoutDecorator to decorate widgets from different sections using a TextView. This LayoutDecorator recognizes the following parameters, configured using TextViewLayoutDecoratorConfig:

Property Description
layout Metawidget Layout to use for laying out the sections, for example org.metawidget.android.widget.layout.TableLayout.
style Android style (i.e. @com.myapp:style/section) to apply to section breaks. The style should be defined within res/values/styles.xml. For example:
<style name="section">
	<item name="android:textSize">20sp</item>
	<item name="android:paddingTop">10px</item>
	<item name="android:paddingBottom">5px</item>
</style>

9. How To's

This chapter contains 'recipes' for various scenarios you may encounter when using Metawidget.

9.1 Order Properties

Metawidget supports several approaches to control the order of properties in the UI, depending on your architecture and your preference:

  • Annotate the properties with @UiComesAfter, use MetawidgetAnnotationInspector in your CompositeInspector chain, and use ComesAfterInspectionResultProcessor. This is the default approach.

  • Use one of the XML-based Inspectors (such as XmlInspector or HibernateInspector) as the first Inspector in your CompositeInspector chain. XML nodes are inherently ordered.

  • Compile your business model with debug information turned on, and use JavassistPropertyStyle. This approach uses Javassist to extract line numbering information, and order the properties in source file order.

  • Write your own InspectionResultProcessor. This gives you a range of options, including: sorting properties based on a page property (see Section 2.3.5, “Implementing Your Own InspectionResultProcessor”); sorting properties based on a different notation (such as Dewey Decimal); defining property order at the class (rather than the getter) level, and many more. For complete worked examples see this blog entry.

9.2 View The Same Object In Different Ways

Say you have an object Employee:

public class Employee {
			
	...
	
	@Column( nullable = false )
	public String getName() {
		return mName;
	}

	public boolean isManager() {
		return mManager;
	}
}

You have a requirement to construct both a screen to view an individual employee, and a screen to search for employees. For the latter you would like to reuse the Employee class but display its properties a little differently. For example, your individual employee screen may use JpaInspector/HibernateInspector to pick up on the @Column annotation and mark the name property as 'required'. For your search screen you may not want this.

You have some options:

  • You could create a second metawidget.xml (perhaps metawidget-search.xml) that does not include JpaInspector/HibernateInspector and use it for your search screen:

    <m:metawidget value="#{employee}" config="metawidget-search.xml"/>

    You can see an example of this in Section 1.4.7, “Swing AppFramework Example”.

  • You could add an Inspector like XmlInspector to the end of your CompositeInspector chain and explictly set required="false". This would override the inspection result from JpaInspector/HibernateInspector. Then you could use this in a metawidget-search.xml as described above.

  • You could add an InspectionResultProcessor like FacesInspectionResultProcessor or JexlInspectionResultProcessor to override the required attribute conditionally based on some Expression Language expression.

  • You could create a custom annotation and custom Inspector (see Section 2.2.7, “Implementing Your Own Inspector (Java)” and a custom WidgetBuilder that excluded annotated properties (see Section 2.4.9, “Implementing Your Own WidgetBuilder”). Then you could use this in a metawidget-search.xml as described above.

  • You could create another class EmployeeSearch that mirrors Employee. This would introduce some duplication, but may work better if the search properties are substantially different to the employee properties. For example now isManager could be a Boolean (big 'B') so that you could search for 'is a manager', 'is not a manager' and 'don't care'.

9.3 Deploy As An Enterprise ARchive (EAR)

If your application is deployed as a Enterprise ARchive (EAR), typically your domain objects will be in an EJB tier and your UI screens in a WAR tier. To use Metawidget-specific annotations on your domain objects (such as @UiComesAfter) you must therefore deploy Metawidget under ear/lib. Depending on your application server, you may encounter a few problems doing this. Specifically:

  • Some application servers have bugs whereby TLD files cannot be detected under ear/lib, only under WEB-INF/lib. A workaround is to copy just the TLD file you need (eg. metawidget-faces.tld) into your WEB-INF folder.

  • Some application servers have classloader issues if a class is not loaded by the correct classloader (eg. JSF components must be loaded by the WAR classloader) or if the same class is loaded by multiple classloaders (eg. annotation classes loaded by both the EJB and the WAR classloaders may not be recognised when passed between tiers).

  • Some application servers scan the classpath on startup, looking for JAR annotations. They may struggle with Metawidget classes that have optional dependencies (eg. they may throw ClassNotFoundException on SpringMetawidgetTag if you are not shipping Spring with your application).

All these problems can be resolved by not using metawidget-all.jar. Instead, switch to using fine-grained dependencies (eg. metawidget-facesannotation.jar in the EJB tier, metawidget-faces.jar in the WAR tier). A typical setup might be:

ear/lib                              # EJB tier annotations	
   metawidget-annotation.jar
   metawidget-core.jar
   metawidget-facesannotation.jar
ear/war/WEB-INF/lib                  # WAR tier inspectors/widgets
   metawidget-faces.jar
   metawidget-hibernatevalidator.jar
   metawidget-jpa.jar
   metawidget-richfaces.jar
   metawidget-tomahawk.jar

To use fine-grained dependencies, specify dependencies on a per-technology basis using Maven. For example:

  • org.metawidget.modules.faces:metawidget-richfaces
  • org.metawidget.modules.swing:metawidget-beansbinding
  • org.metawidet.modules:metawidget-jpa

Maven will automatically drag in related dependencies for you, such as org.metawidget.modules:metawidget-core. You can browse all available fine-grained dependencies at http://repository.jboss.org/nexus/content/groups/public-jboss/org/metawidget/modules. If you are not using Maven, you can also download and deploy the fine-grained dependencies manually.

9.4 Inspect Remotely

Metawidget inspects back-end metadata and creates front-end UI widgets. If your application is split over multiple tiers, however, sometimes the back-end metadata is not accessible from the front-end. For example, annotations (such as JPA ones) are designed to 'fall away' if the class is transferred to a tier without their JAR (such as ejb3-persistence.jar) in its classpath. Equally, configuration files may not be accessible across tiers.

Metawidget supports these situations. Because each remoting environment is different, however, you will need to add a little code yourself. Every Inspector returns an XML string, which is inherently serializable and safe to pass across tiers. Therefore, to run inspection remotely:

  • create a back-end class suited to your environment, such as an EJB Session Bean. Have the back-end class instantiate an Inspector - either programmatically:

    XmlInspectorConfig config = new XmlInspectorConfig();
    config.setInputStream( getClass().getResourceAsStream( "metawidget-backend-metadata.xml" ));
    Inspector inspector = new XmlInspector( config );

    Or by using a metawidget.xml file and the ConfigReader helper class:

    inspector = ConfigReader.configure( "backend-metawidget.xml", Inspector.class );
  • have the back-end class declare the Inspector interface. Implement the interface by delegating to the Inspector you just instantiated.

  • depending on your environment, it may futher be necessary to create a front-end class (a proxy). Have it declare the Inspector interface. Implement the interface by remoting to the back-end class and returning the XML string.

  • set the Inspector on the Metawidget by using the setInspector method.

An example of this technique can be seen in GwtRemoteInspectorProxy and GwtRemoteInspectorImpl.

[Tip]Note
All Inspectors are immutable. Therefore you only need one Inspector for your entire application. Some remoting architectures support 'singletons' or 'service beans' well suited to this.

9.4.1 Combine Remote Inspections

If your architecture is strongly separated, some metadata may only be available in one tier (e.g. JPA annotations in the back-end) and some only available in another tier (e.g. struts-config.xml in the front-end).

For this, CompositeInspector supplies an overloaded method outside the normal Inspector interface. The overloaded CompositeInspector.inspect method takes an additional XML string of inspection results, and merges forthcoming inspection results with it.

Therefore, to combine metadata from different tiers:

  • create a front-end class that implements the Inspector interface

  • implement the interface by first remoting to the back-end class and returning the XML string, as before

  • next, delegate to a CompositeInspector to inspect the front-end, passing it the XML string from the back-end as a starting point

9.5 Improve Performance

Performance is very important to Metawidget. Whilst generating a UI dynamically - as opposed to hard coding it statically - is always likely to involve a performance tradeoff, Metawidget supports a number of techniques to help minimize this.

9.5.1 JAR Size

Metawidget has no mandatory third-party JAR dependencies and is highly modular. This allows the standard metawidget-all.jar to be repackaged for different environments to save JAR size.

For example, the addressbook-swing-applet Maven POM uses fine-grained Metawidget dependencies (as an alternative to metawidget-all.jar) to include only those modules necessary for the applet. Similarly, the addressbook-android Maven POM includes only those Android modules necessary for the Android Address Book.

Fine-grained dependencies are only available through Maven. They take advantage of Maven's dependency resolution to drag in related dependencies for you, such as org.metawidget.modules:metawidget-core. To use them, specify dependencies on a per-technology basis instead of using metawidget-all. For example:

  • org.metawidget.modules.faces:metawidget-richfaces

  • org.metawidget.modules.swing:metawidget-beansbinding

  • org.metawidet.modules:metawidget-jpa

To browse all available fine-grained dependencies visit http://repository.jboss.org/nexus/content/groups/public-jboss/org/metawidget/modules.

9.5.2 Memory Usage

All Inspectors, PropertyStyles, ActionStyles, InspectionResultProcessors, WidgetBuilders, WidgetProcessors and Layouts are immutable. This means you only need a single instance of them for your entire application. If you are using metawidget.xml then ConfigReader takes care of this for you, but if you are instantiating them yourself in Java code you should reuse instances to save memory.

9.5.3 DomInspector and DomInspectionResultProcessor

By default the Inspector and InspectionResultProcessor interfaces operate on XML strings. These are a great lowest-common-denominator, perfect for passing between disparate technologies and tiers in order to allow maximum flexibility in what can be inspected.

However, serializing to and from XML strings can be expensive. This is especially true for those Inspectors and InspectionResultProcessors that maintain their results internally in a DOM object, because the Inspector or InspectionResultProcessor must serialize its DOM to a string, whereupon the Metawidget typically deserializes it back again to a DOM.

To optimize this scenario, Inspectors and InspectionResultProcessors can optionally implement DomInspector and DomInspectionResultProcessor respectively. These interfaces allow them to expose their DOM directly, skipping the serialization/deserialization step.

If your Inspector extends BaseObjectInspector or BaseXmlInspector, this optimization is implemented for you.

9.5.4 Rebinding

For Metawidgets that do not use automatic binding, the general approach is to call setToInspect and then call setValue on each generated widget to populate values. This technique has an implicit side effect: the values can also be repopulated as many times as required from different objects, without re-calling setToInspect. This allows the Metawidget to be generated once and reused many times, mitigating the performance cost of generation.

For Metawidgets that do use automatic binding, however, setValue is never used. Setting new values requires re-calling setToInspect (and re-running generation) for every different object.

To avoid this some WidgetProcessors support a second, lightweight version of setToInspect called rebind. Using rebind, a WidgetProcessor can update the values in the generated widgets without re-running generation. This allows the Metawidget to be generated once and reused many times.

The downside of rebind is that the rebound object must have exactly the same set of property names as the original object. It becomes the responsibility of the caller to ensure this consistency.

For an example of using rebinding, see the GWT Address Book sample application.

10. Troubleshooting

This chapter contains solutions for various common problems you may encounter when using Metawidget.

10.1 General

10.1.1 Metawidget Doesn't Work!

The first step to troubleshooting is to enable DEBUG or TRACE logging. The exact procedure for doing this varies depending on your environment, but here are some suggestions:

  • If your application does not use Apache Commons Logging, Metawidget will default to java.util.Logging. Locate the logging.properties file (eg. <JAVA_HOME>/jre/lib/logging.properties) and edit it. Set .level=FINE and java.util.logging.ConsoleHandler.level=FINE.

  • If your application uses Apache Log4J (possibly underneath Apache Commons Logging) locate the log4j.xml file (eg. /jboss-5.1.0.GA/server/default/conf/jboss-log4j.xml) and edit it. Remove any reference to <param name="Threshold" ...>.

  • If your application uses JBoss Logging (ie. under JBoss AS 6) locate the jboss-logging.xml file (eg. /jboss-6.0.0.Final/server/default/deploy/jboss-logging.xml) and edit it. Under the <console-handler> section set <level name="DEBUG"/>. Under the <root-logger> section set <level name="DEBUG"/>.

  • If you are running Tomcat 7 under Eclipse, make sure you edit the server's launch configuration and include a VM argument of -Djava.util.logging.config.file=<tomcat path>/conf/logging.properties. Then edit <tomcat path>/conf/logging.properties as described above.

  • If you are running JBoss AS 7, you will need to edit the <subsystem xmlns="urn:jboss:domain:logging:1.1"> section of standalone.xml. Under the <console-handler> section set <level name="DEBUG"/>. Under the <root-logger> section set <level name="DEBUG"/>.

10.2 Inspectors

10.2.1 General Inspectors

My Inspector does not return anything

If your custom Inspector returns no <entity> blocks, or returns <entity> blocks with the wrong type attribute, Metawidget will ignore their result. Try wrapping your Inspector inside CompositeInspector and enabling TRACE logging, to see what your Inspector is returning.

10.2.2 Annotation-Based Inspectors

I get "java.lang.TypeNotPresentException"

If you are using Oracle's implementation of Java, Metawidget's annotation support requires Java 5.0u6 or later, which includes a fix for this bug (Bug Parade ID: 6322301).

My inspector is not finding my annotations

First, if you are using custom annotations check they are marked to 'retain at runtime':

@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.FIELD, ElementType.METHOD } )	
public @interface MyAnnotation {

	String value();
}

Next, remember that annotations are designed to 'silently fall away' in environments that do not support them: they never throw ClassDefNotFoundError. For example, if a JPA-annotated class is transferred to an application tier without ejb3-persistence.jar (or equivalent) in its classpath, the JPA annotations will disappear. If this is the case, either add the appropriate JAR to the tier, or consider implementing a remote inspector (see Section 9.1, “Order Properties”).

Finally, note that by default Metawidget discovers annotations placed on public fields and public getter/setter methods, but not on private fields. Metawidget must work with public properties because the various widget libraries, binding frameworks and validation frameworks it wires together all need to access public properties. And because there is no standardized way to infer which private field relates to which public getter/setter method, Metawidget cannot relate the two. Either:

10.3 Web Metawidgets

10.3.1 I put <m:metawidget > in my page but nothing appears

View the source of the final Web page that is produced. If m:metawidget appears in it, the tag is not being processed. Check you have correctly declared the m: namespace at the top of your JSP/Facelets page. Also check you have correctly deployed metawidget-all.jar into WEB-INF/lib.

If m:metawidget does not appear in the final Web page, the tag is successfully being procesed. Check whether a 'blank' Metawidget is being produced (i.e. an empty table if you are using a table-based layout, some empty divs if you are using a div-based layout). In so, check the value expression is returning a non-null value. For example, if your JSF tag is <m:metawidget value="#{foo.bar}"/>, check that #{foo.bar} does not return null. Equally, if your JSP tag is <m:metawidget value="foo"/>, check you have put foo into the scope.

10.3.2 Java Server Faces

I have annotated my getter, but Metawidget is ignoring my annotations

If your getter code is...

public class Foo {

	private String mBar = "Hello";
	
	@UiLabel( "My Bar" )
	public String getBar() {
	
		return mBar;
	}
}

...and your code JSP/Facelets code is...

<m:metawidget value="#{foo.bar}"/>

Then by default the @UiLabel annotation will be ignored. This is because the EL expression #{foo.bar} evaluates directly to an object (in this case a String) so Metawidget will not consider any parent objects or their getter methods. There are two options:

  • Change the EL expression to <m:metawidget value="#{foo}"/> and let Metawidget traverse the child property

  • Use <m:metawidget value="#{foo.bar}" inspectFromParent="true"/> to force Metawidget to unpack the EL expression and evaluate the 'parent' #{foo}

Note that if #{foo.bar} evaluates to null then Metawidget will unpack and check the parent automatically, but this is simply a niceity.

My parameterized List renders as a single column

If your getter code is...

public class Foo {

	public List<Bar> getBars() {
	
		return mBars;
	}
}

...and your code JSP/Facelets code is...

<m:metawidget value="#{foo.bars}"/>

Then by default the <Bar> parameterized type will be ignored. This is because the EL expression #{foo.bars} evaluates directly to the Collection. Metawidget will not consider any parent objects or their getter methods. There are two options:

  • Change the EL expression to <m:metawidget value="#{foo}"/> and let Metawidget traverse the child property

  • Use <m:metawidget value="#{foo.bars}" inspectFromParent="true"/> to force Metawidget to unpack the EL expression and evaluate the 'parent' #{foo}

Note that if #{foo.bars} evaluates to null then Metawidget will unpack and check the parent automatically, but this is simply a niceity.

I get "View could not be restored"

Depending on your environment and JSF implementation, you may encounter problems restoring the ViewState after POSTback. If this happens, try setting...

<context-param>
	<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
	<param-value>client</param-value>
</context-param>

...in your web.xml.

I get "The class ... does not have a readable property"

Depending on your environment, you may encounter problems with proxies introducing additional properties into your objects. These additional properties cannot be read by JSF. For example a @RequestScoped Weld object may get wrapped with a WeldClientProxy that introduces a handler property. If this happens, try setting...

<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
				config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<propertyStyle>
		<javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean"
				config="JavaBeanPropertyStyleConfig">
			<excludeName>
				<array>
					<string>handler</string>
				</array>
			</excludeName>
		</javaBeanPropertyStyle>
	</propertyStyle>
</propertyTypeInspector>

...in your metawidget.xml. This will ignore properties named handler. You may find the refId convention useful for making this declaration less verbose, see Section 2.7.5, “Understanding Immutability”.

10.3.3 Struts

I get "Cannot find bean org.apache.struts.taglib.html.BEAN in any scope"

StrutsMetawidgetTag creates native Struts widgets, such as <html:text>, but does not create the surrounding Struts form. Make sure your Metawidget tag is enclosed in a <html:form> tag and the Struts HTML taglib is included at the top of the page.

I see "MultipartRequestHandler", "ServletWrapper" and other weird names

If you use PropertyTypeInspector to inspect your ActionForm-based classes, by default it will discover properties from the org.apache.struts.action.ActionForm base class, such as getMultipartRequestHandler. To prevent this, configure metawidget.xml:

<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
	config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<propertyStyle>
		<javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean"
			config="org.metawidget.inspector.impl.propertystyle.BasePropertyStyleConfig">
			<excludeBaseType>
				<pattern>^(java|javax|org\.apache\.struts)\..*$</pattern>
			</excludeBaseType>
		</javaBeanPropertyStyle>
	</propertyStyle>
</propertyTypeInspector>

Epilogue

That concludes the Metawidget User Guide and Reference Documentation.

For further documentation - including forums, bug reports and information on how to contribute - please visit http://metawidget.org.

Thank you for using Metawidget!