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.