Friday, March 15, 2013

XML Schema, WSDL and SOAP UI Generator

Following a recent request, the next release of Metawidget will add support for XML Schema (XSD) files. It should be useful for anybody wanting to automatically generate User Interfaces from XSD schemas, Web Services Description Language (WSDL) files, or SOAP.

To use, simply take an existing XML Schema or WSDL file:

<?xml version="1.0"?>
<wsdl:definitions ...>

   ...
   <wsdl:types>
      <xsd:schema>
         <xsd:element name="GetEndorsingBoarder">
            <xsd:complexType>
               <xsd:sequence>
                  <xsd:element name="name" type="string" minOccurs="1" />
                  <xsd:element name="manufacturer" type="string" />
                  <xsd:element name="model" type="xs:integer" />
                  <xsd:element name="quantity">
                     <xsd:simpleType>
                        <xsd:restriction base="xs:integer">
                           <xsd:minInclusive value="0" />
                           <xsd:maxInclusive value="8" />
                        </xsd:restriction>
                     </xsd:simpleType>
                  </xsd:element>
                  <xsd:element name="active" type="xs:boolean" />
               </xsd:sequence>
            </xsd:complexType>
         </xsd:element>

   ...

</wsdl:definitions>

...point Metawidget at it...

public class Main {

   public static void main( String[] args ) {

      final SwingMetawidget metawidget = new SwingMetawidget();
      metawidget.setInspector( new WsdlInspector(
         new XmlSchemaInspectorConfig().setInputStream( new SimpleResourceResolver()
            .openResource( "endorsementSearch.wsdl" ) ) ) );
      metawidget.addInspectionResultProcessor( new XmlSchemaToJavaTypeMappingProcessor() );
      metawidget.setPath( "GetEndorsingBoarder" );


      JFrame frame = new JFrame( "Metawidget WSDL" );
      frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      frame.getContentPane().add( metawidget );
      frame.setSize( 400, 210 );
      metawidget.setBorder( BorderFactory.createEmptyBorder( 5,5,5,5 ) );
      frame.setVisible( true );
   }
}

...and this will create the screenshot seen below:

Getting data into the UI components and back out again will vary depending on your requirements (choice of UI toolkit, choice of data storage etc). Here's a simple implementation that reads/writes from a DOM document to Swing JComponents:

public class DocumentBindingProcessor
   implements AdvancedWidgetProcessor<JComponent, SwingMetawidget> {

   //
   // Public methods
   //

   @Override
   public void onStartBuild( SwingMetawidget metawidget ) {

      getWrittenComponents( metawidget ).clear();
   }

   /**
    * Retrieve the values from the Document and put them in the Components.
    */

   @Override
   public JComponent processWidget( JComponent component, String elementName, Map<String, String> attributes, SwingMetawidget metawidget ) {

      String attributeName = attributes.get( NAME );
      getWrittenComponents( metawidget ).put( attributeName, component );

      // Fetch the value...

      Document toInspect = metawidget.getToInspect();
      Element root = toInspect.getDocumentElement();
      Element valueNode = XmlUtils.getChildNamed( root, attributeName );

      if ( valueNode == null ) {
         return component;
      }

      // ...and apply it to the component. For simplicity, we won't worry about converters

      String componentProperty = metawidget.getValueProperty( component );
      ClassUtils.setProperty( component, componentProperty, valueNode.getTextContent() );

      return component;
   }

   @Override
   public void onEndBuild( SwingMetawidget metawidget ) {

      // Do nothing
   }

   /**
    * Store the values from the Components back into the Document.
    */

   public void save( SwingMetawidget metawidget ) {

      Document toInspect = metawidget.getToInspect();
      Element root = toInspect.getDocumentElement();

      for ( Map.Entry<String, JComponent> entry : getWrittenComponents( metawidget ).entrySet() ) {

         JComponent component = entry.getValue();
         String componentProperty = metawidget.getValueProperty( component );
         Object value = ClassUtils.getProperty( component, componentProperty );

         Element valueNode = XmlUtils.getChildNamed( root, entry.getKey() );

         if ( valueNode == null ) {
            continue;
         }

         valueNode.setTextContent( (String) value );
      }
   }

   //
   // Private methods
   //

   /**
    * During load-time we keep track of all the components. At save-time we write them all back
    * again.
    */

   private Map<String, JComponent> getWrittenComponents( SwingMetawidget metawidget ) {

      @SuppressWarnings( "unchecked" )
      Map<String, JComponent> writtenComponents = (Map<String, JComponent>) metawidget.getClientProperty( DocumentBindingProcessor.class );

      if ( writtenComponents == null ) {
         writtenComponents = CollectionUtils.newHashMap();
         metawidget.putClientProperty( DocumentBindingProcessor.class, writtenComponents );
      }

      return writtenComponents;
   }
}

To use it, simply add it into your Metawidget's pipeline:

metawidget.addInspectionResultProcessor( ... );
metawidget.addWidgetProcessor( new DocumentBindingProcessor() );
String xml = "<endorsingBoarder><name>Richard</name><manufacturer>Kennard Consulting</manufacturer></endorsingBoarder>";

final Document document = XmlUtils.documentFromString( xml );
metawidget.setToInspect( document );

metawidget.setPath( "GetEndorsingBoarder" );

...

metawidget.add( new JButton( new AbstractAction( "Save" ) {

   public void actionPerformed( ActionEvent e ) {
      metawidget.getWidgetProcessor( DocumentBindingProcessor.class ).save( metawidget );
      System.out.println( XmlUtils.documentToString( document, true ));

   }
} ));

And you'll get this:

Feedback welcome!

Monday, March 11, 2013

Lightweight JSON to User Interface (UI) generator

I write a lot on this blog about customizing Metawidget for different scenarios. But it's worth stressing Metawidget also works as a lightweight, no fuss solution out-of-the-box.

For example, say you just wanted to quickly render a JSON object on the client. Here's the entire code:

<!DOCTYPE HTML>
<html>
   <head>
      <script src="http://metawidget.org/js/3.4/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"></div>
      <script type="text/javascript">
         var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ));
         mw.toInspect = {
            firstname: 'Homer',
            surname: 'Simpson',
            age: 36
         };
         mw.buildWidgets();

      </script>
   </body>
</html>

This will handily render:

Of course you can go much (much) deeper, but for once I'll resist the temptation to show that. If you're interested, start with the tutorial here.

Wednesday, March 6, 2013

JavaScript Form Generator: Metawidget v3.2

Version 3.2 of Metawidget, the JavaScript form generator is now available!

This release was focused on:
As always, the best place to start is the Reference Documentation:


http://metawidget.org/doc/reference/en/pdf/metawidget.pdf


Your continued feedback is invaluable to us. Please download it and let us know what you think.

Thursday, February 28, 2013

Metawidget meets FreeMarker

A recent conversation with the JBoss AeroGear team inspired me to add FreeMarker support to Metawidget. This should be useful for those wanting to declare static layouts using FreeMarker templates rather than Java code.

To use the new FreeMarkerLayout, simply configure it:

StaticHtmlMetawidget metawidget = new StaticHtmlMetawidget();

BaseObjectInspectorConfig config = new BaseObjectInspectorConfig()
   .setPropertyStyle( new StaticPropertyStyle() );

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

metawidget.setLayout( new FreemarkerLayout( new FreemarkerLayoutConfig()
   .setDirectoryForTemplateLoading( "./src/main/resources" )
   .setTemplate( "template.ftl" ) )
);

Or you can use metawidget.xml if your prefer...

<staticHtmlMetawidget xmlns="java:org.metawidget.statically.html">
   ...
   <layout>
      <freemarkerLayout xmlns="java:org.metawidget.statically.freemarker.layout"
         config="FreemarkerLayoutConfig">
         <directoryForTemplateLoading>
            <string>./src/main/resources</string>
         </directoryForTemplateLoading>
         <template>
            <string>template.ftl</string>
         </template>
      </freemarkerLayout>

   </layout>
</staticHtmlMetawidget>

...define your FreeMarker template...

<table>
   <tbody>
      <#list widgets as widget><tr>
         <th>${widget.label}:</th>
         <td>${widget.xml}</td>
         <#if (widget.attributes.required!"false") == "true">
         <td>*</td>
         <#else>
         <td></td>
         </#if>
      </tr></#list>
   </tbody>
</table>

...and point Metawidget at your business class...

metawidget.setPath( Person.class.getName() );

StringWriter writer = new StringWriter();
metawidget.write( writer );
System.out.println( writer );

Metawidget will use its existing Inspectors, InspectionResultProcessors, WidgetBuilders and WidgetProcessors to prepare the widgets - but will lay them out using your FreeMarker template.

I've put together a complete example you can download here. Feedback welcome!

Sunday, February 10, 2013

AngularJS Improvements: Metawidget v3.1

Version 3.1 of Metawidget, the dynamic form generator is now available!

This release was focused on:
  • Improvements to pure JavaScript, JQuery UI and Angular JS Metawidgets
  • Bug fixes, documentation and unit tests
As always, the best place to start is the Reference Documentation:


http://metawidget.org/doc/reference/en/pdf/metawidget.pdf


Your continued feedback is invaluable to us. Please download it and let us know what you think.

Friday, February 1, 2013

Metawidget and REST

When using one of the new, pure JavaScript Metawidgets 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.

Thursday, January 31, 2013

AngularJS Form Generator: Metawidget v3.0

Version 3.0 of Metawidget, the dynamic form generator is now available!

This release was focused on:
As always, the best place to start is the Reference Documentation:


http://metawidget.org/doc/reference/en/pdf/metawidget.pdf


Your continued feedback is invaluable to us. Please download it and let us know what you think.