Following on from Part 1 and Part 2, I thought I'd blog some of the other field ordering preferences I've encountered.Out of the box, Metawidget supplies UiComesAfter and ComesAfterInspectionResultProcessor to let you order your fields on a 'per domain' basis. But of course there are other approaches.
This one is from the Naked Objects .NET guys. In this presentation you can see how they use a .NET MemberOrder attribute to specify field order as 1, 2, 3 etc. Example implementation below. Some points to note:
- It's in Swing so you can just cut and paste and run it
- It defines a custom annotation (UiOrder) and custom Inspector (OrderInspector) to detect it
- It defines an OrderInspectionResultProcessor that sorts the properties/actions
package com.myapp;
import static org.metawidget.inspector.InspectionResultConstants.*;
import java.lang.annotation.*;
import java.util.*;
import javax.swing.*;
import org.metawidget.inspectionresultprocessor.iface.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.impl.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.swing.*;
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.setInspector( new CompositeInspector( new CompositeInspectorConfig().setInspectors(
new PropertyTypeInspector(),
new MetawidgetAnnotationInspector(),
new OrderInspector() ) ) );
metawidget.addInspectionResultProcessor( new OrderInspectionResultProcessor() );
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 Person {
@UiOrder( 1 )
public String name;
@UiOrder( 2 )
public int age;
@UiOrder( 3 )
public boolean retired;
@UiOrder( 4 )
@UiLarge
public String notes;
}
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.FIELD, ElementType.METHOD } )
static @interface UiOrder {
int value();
}
static class OrderInspector
extends BaseObjectInspector {
@Override
protected Map<String, String> inspectTrait( Trait trait )
throws Exception {
Map<String, String> attributes = CollectionUtils.newHashMap();
UiOrder order = trait.getAnnotation( UiOrder.class );
if ( order != null ) {
attributes.put( "order", String.valueOf( order.value() ) );
}
return attributes;
}
}
static class OrderInspectionResultProcessor
implements InspectionResultProcessor<SwingMetawidget> {
public String processInspectionResult( String inspectionResult, SwingMetawidget metawidget, Object toInspect, String type, String... names ) {
try {
// Start a new document
//
// (Android 1.1 did not cope well with shuffling the nodes of an existing document)
Document newDocument = XmlUtils.newDocument();
Element newInspectionResultRoot = newDocument.createElementNS( NAMESPACE, ROOT );
Document document = XmlUtils.documentFromString( inspectionResult );
Element inspectionResultRoot = document.getDocumentElement();
XmlUtils.setMapAsAttributes( newInspectionResultRoot, XmlUtils.getAttributesAsMap( inspectionResultRoot ) );
newDocument.appendChild( newInspectionResultRoot );
Element entity = (Element) inspectionResultRoot.getChildNodes().item( 0 );
Element newEntity = newDocument.createElementNS( NAMESPACE, ENTITY );
XmlUtils.setMapAsAttributes( newEntity, XmlUtils.getAttributesAsMap( entity ) );
newInspectionResultRoot.appendChild( newEntity );
// Record all traits (ie. properties/actions) that have an order
Map<Integer, Element> traitsWithOrder = CollectionUtils.newTreeMap();
NodeList traits = entity.getChildNodes();
for ( int loop = 0, length = traits.getLength(); loop < length; loop++ ) {
Node node = traits.item( loop );
if ( !( node instanceof Element ) ) {
continue;
}
Element trait = (Element) node;
// (if no order, move them across to the new document)
if ( !trait.hasAttribute( "order" ) ) {
newEntity.appendChild( XmlUtils.importElement( newDocument, trait ) );
continue;
}
traitsWithOrder.put( Integer.valueOf( trait.getAttribute( "order" ) ), trait );
}
// Output the traits in TreeMap order
for ( Element trait : traitsWithOrder.values() ) {
newEntity.appendChild( XmlUtils.importElement( newDocument, trait ) );
}
// Return the new document
return XmlUtils.documentToString( newInspectionResultRoot.getOwnerDocument(), false );
} catch ( Exception e ) {
throw InspectionResultProcessorException.newException( e );
}
}
}
}
import static org.metawidget.inspector.InspectionResultConstants.*;
import java.lang.annotation.*;
import java.util.*;
import javax.swing.*;
import org.metawidget.inspectionresultprocessor.iface.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.impl.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.swing.*;
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.setInspector( new CompositeInspector( new CompositeInspectorConfig().setInspectors(
new PropertyTypeInspector(),
new MetawidgetAnnotationInspector(),
new OrderInspector() ) ) );
metawidget.addInspectionResultProcessor( new OrderInspectionResultProcessor() );
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 Person {
@UiOrder( 1 )
public String name;
@UiOrder( 2 )
public int age;
@UiOrder( 3 )
public boolean retired;
@UiOrder( 4 )
@UiLarge
public String notes;
}
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.FIELD, ElementType.METHOD } )
static @interface UiOrder {
int value();
}
static class OrderInspector
extends BaseObjectInspector {
@Override
protected Map<String, String> inspectTrait( Trait trait )
throws Exception {
Map<String, String> attributes = CollectionUtils.newHashMap();
UiOrder order = trait.getAnnotation( UiOrder.class );
if ( order != null ) {
attributes.put( "order", String.valueOf( order.value() ) );
}
return attributes;
}
}
static class OrderInspectionResultProcessor
implements InspectionResultProcessor<SwingMetawidget> {
public String processInspectionResult( String inspectionResult, SwingMetawidget metawidget, Object toInspect, String type, String... names ) {
try {
// Start a new document
//
// (Android 1.1 did not cope well with shuffling the nodes of an existing document)
Document newDocument = XmlUtils.newDocument();
Element newInspectionResultRoot = newDocument.createElementNS( NAMESPACE, ROOT );
Document document = XmlUtils.documentFromString( inspectionResult );
Element inspectionResultRoot = document.getDocumentElement();
XmlUtils.setMapAsAttributes( newInspectionResultRoot, XmlUtils.getAttributesAsMap( inspectionResultRoot ) );
newDocument.appendChild( newInspectionResultRoot );
Element entity = (Element) inspectionResultRoot.getChildNodes().item( 0 );
Element newEntity = newDocument.createElementNS( NAMESPACE, ENTITY );
XmlUtils.setMapAsAttributes( newEntity, XmlUtils.getAttributesAsMap( entity ) );
newInspectionResultRoot.appendChild( newEntity );
// Record all traits (ie. properties/actions) that have an order
Map<Integer, Element> traitsWithOrder = CollectionUtils.newTreeMap();
NodeList traits = entity.getChildNodes();
for ( int loop = 0, length = traits.getLength(); loop < length; loop++ ) {
Node node = traits.item( loop );
if ( !( node instanceof Element ) ) {
continue;
}
Element trait = (Element) node;
// (if no order, move them across to the new document)
if ( !trait.hasAttribute( "order" ) ) {
newEntity.appendChild( XmlUtils.importElement( newDocument, trait ) );
continue;
}
traitsWithOrder.put( Integer.valueOf( trait.getAttribute( "order" ) ), trait );
}
// Output the traits in TreeMap order
for ( Element trait : traitsWithOrder.values() ) {
newEntity.appendChild( XmlUtils.importElement( newDocument, trait ) );
}
// Return the new document
return XmlUtils.documentToString( newInspectionResultRoot.getOwnerDocument(), false );
} catch ( Exception e ) {
throw InspectionResultProcessorException.newException( e );
}
}
}
}


I've
For me, as I approach the end of any given day's programming, I am usually in one of two frames of mind:
On the latest episode of the
So while Metawidget does advocate removing the boilerplate code and error-prone tedium of using visual tools (like Matisse - sorry
Of course Joe, being passionate about his pixels, would also be pleased to hear Metawidget does this without hiding or restricting your existing UI framework. So you still use your preferred tools to get the exact look your users require, only now you can automate a lot of the drudgery. 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 only tries to help the creation of those areas of UI design that are already rigidly defined - it does not overlap with those areas involving subjectivity, creativity or aesthetics. This may come as a blow to 