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.