Tuesday, July 20, 2010

Customizing Which Form Fields Are Displayed: Part 5

Following on from parts 1, 2, 3 and 4, I thought I'd blog some of the other field ordering preferences I've encountered.

This one is from the Naked Objects .ORG team, who provide (though don't necessarily recommend) support for declaring field order at the class level. 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 (UiFieldOrder) and custom Inspector (FieldOrderInspector) to detect it

  • The Inspector overrides inspectEntity (ie. class-level) rather than inspectTrait (ie. field-level)
  • It defines a FieldOrderInspectionResultProcessor 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 FieldOrderInspector() ) ) );
      metawidget.addInspectionResultProcessor( new FieldOrderInspectionResultProcessor() );
      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 );
   }

   @UiFieldOrder( { "name", "age", "retired", "notes" } )
   static class Person {

      public String   name;

      public int      age;

      public boolean   retired;

      @UiLarge
      public String   notes;
   }

   @Retention( RetentionPolicy.RUNTIME )
   @Target( { ElementType.TYPE } )
   static @interface UiFieldOrder {

      String[] value();
   }


   static class FieldOrderInspector
      extends BaseObjectInspector {

      @Override
      protected Map<String, String> inspectEntity( Class<?> declaredClass, Class<?> actualClass )
         throws Exception {

         Map<String, String> attributes = CollectionUtils.newHashMap();
         UiFieldOrder fieldOrder = actualClass.getAnnotation( UiFieldOrder.class );

         if ( fieldOrder != null ) {
            attributes.put( "field-order", ArrayUtils.toString( fieldOrder.value() ) );
         }

         return attributes;
      }
   }

   static class FieldOrderInspectionResultProcessor
      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 );

            // Look up the field-order (if any)

            String fieldOrder = entity.getAttribute( "field-order" );

            if ( fieldOrder == null ) {
               return inspectionResult;
            }

            String[] fieldOrderArray = ArrayUtils.fromString( fieldOrder );

            for ( String field : fieldOrderArray ) {
               Element trait = XmlUtils.getChildWithAttributeValue( entity, NAME, field );
               newEntity.appendChild( XmlUtils.importElement( newDocument, trait ) );
            }

            // Return the new document

            return XmlUtils.documentToString( newInspectionResultRoot.getOwnerDocument(), false );

         } catch ( Exception e ) {
            throw InspectionResultProcessorException.newException( e );
         }
      }
   }
}

0 comments: