Wednesday, September 30, 2009

MY planned visit to my homeland - my beloved land

Hi gurus and non-gurus :P hehe like me

today I am sharing my feelings with all of you.. who are living far far away from the land they belongs to... I am also the person who is living to the align land... there is some big magnetic attraction from our motherland... and now that attraction is attracting me toward itself.

In short I am going to Pakistan for one month... wowowo I cannot imagine how much happy I am... there is special kind of sweetness in my heart.. making me dreaming....

but yes there is something special in travelling to my hometown.. to see the street where I walk and run to become Me.

wish for my save and happy journey

Thursday, August 13, 2009

Databinding in GWT

Binding is the most discussed issue in the GWT community. Everybody has different solution and different material to support their arguments. When we say binding we say POJO based binding. I am from school of thoughts who loved to see the client interface totally decoupled from the Business layer, passing Business Objects to the User Interface layer will destroy the concept of decoupling. We can achieve the concept of decoupling the UI layer from Business layer if we pass the Map based data from Business layer. Map is key/value pair system in which we consider ‘key’ as the name of domain property and value will be ‘Object’ associated with this property. In this article I will explain the mechanism of creating binding system for the UI widget with the Map based data. We had created the Mab2BeanConvertor utility class which will convert the POJO into HashMap and vice versa. This utility class will take the burden of transformation of the HashMap to POJO and POJO to HashMap.
Now I will explain how we achieved the binding with GWT. We follow the JGoodies style of ValueModel concept of holding the data, and Binder System which will bind the ValueModel to the UI component.
This system has three(3) number of classes i.e.

(i) GWTMapValueModel

(ii) GWTBinding

(iii) GWTWidgetDomainSynchronizer



1. GWTMapValueModel is the value model which holds the value for widget associated to some property of the bean i.e. key of Map.

2. GWTBinding is the main actor of this system which will bind the widget to a key of the map.

3. GWTWidgetDomainSynchronizer will act as the model to keep the widget and map synchronized.



Our GWTMapValueModel is shown below

 
import java.util.Map;
 
/**
 * @author Anees
 *
 */
public class GWTMapValueModel {
                    
             private String key;
             private Map map;
             private GWTWidgetDomainSynchronizer synchronizer ;
                    
             public GWTMapValueModel(String key, Map map){
                                         this.key = key;
                                   this.map = map;
             
                    
                  
             public void setValue(final Object newValue){
                            Object oldValue = map.get(key);
                            map.put(key, newValue);
                            firePropertyChange(oldValue, newValue);
             
                    
                    public Object getValue(){
                                         return map.get(key);
                    }
                    public Map getMap(){
                                         return map;
                    }
                    
                    protected String getKey(){
                                         return key;
                    }
                                         
                    public void createBindableSynch(GWTWidgetDomainSynchronizer    synchronizer){
                                         this.synchronizer = synchronizer;
                    }
                    /**
                     * 
                     * @param oldValue
                     * @param newValue
                     */
                    private void firePropertyChange(Object oldValue, Object newValue){
                                         synchronizer.processGWTMapValueModelChange(oldValue, newValue);
                    }
}
 


the GWTBinding class is shown below with full source code for your guidance:


 
package com.eagle.coders.core.web.gwt.client.ui.bindings;
 
import com.eagle.coders.core.web.gwt.client.ui.form.widgets.RadioButtonWidget;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.datepicker.client.DateBox;
 
/**
 * @author Anees

 *
 */
public class GWTBinding  {
                    private GWTMapValueModel valueModel;
                    private static GWTWidgetDomainSynchronizer sync ;
                    
                    public static void bindTextBox(TextBox textBox, GWTMapValueModel valueModel, String
caseType){
                                         sync = new GWTWidgetDomainSynchronizer(valueModel, textBox);
                                         textBox.addValueChangeHandler(new 
                                                             BindingValueChangeHandler(valueModel, caseType));
                    }
                    
                    public static void bindPasswordBox(PasswordTextBox passwordBox, GWTMapValueModel 
                    valueModel){
                                         sync = new GWTWidgetDomainSynchronizer(valueModel, passwordBox);
                                         passwordBox.addValueChangeHandler(new 
                                                             BindingValueChangeHandler(valueModel, null));
                    }
                    
                    public static void bindRadioButton(RadioButtonWidget radioButton, GWTMapValueModel 
                    valueModel){
                                         sync = new GWTWidgetDomainSynchronizer(valueModel, radioButton);
                                         radioButton.addValueChangeHandler(new 
                                                             BindingValueChangeHandler(valueModel, null));
                    }
                    
                    public static void bindCheckBox(final CheckBox checkBox, GWTMapValueModel valueModel){
                                         sync = new GWTWidgetDomainSynchronizer(valueModel, checkBox);
                                         checkBox.addValueChangeHandler(new 
                                                             BindingValueChangeHandler(valueModel, null));
                    }
                    
                    public static void bindComboBox(final ListBox listBox, GWTMapValueModel valueModel){
                                         sync = new GWTWidgetDomainSynchronizer(valueModel, listBox);
                                         listBox.addChangeHandler(new 
                                                             BindingChangeHandler(valueModel));
                    }
                    
                    public static void bindTextAreaWidget(final TextArea textArea, GWTMapValueModel valueModel){
                                         sync = new GWTWidgetDomainSynchronizer(valueModel, textArea);
                                         textArea.addValueChangeHandler(new 
                                                             BindingValueChangeHandler(valueModel, null));
                    }
                    public static void bindDateBoxWidget(final DateBox dateBox, GWTMapValueModel valueModel){
                                         sync = new GWTWidgetDomainSynchronizer(valueModel, dateBox);
                                         dateBox.addValueChangeHandler(new 
                                                             BindingValueChangeHandler(valueModel, null));
                    }
                    /**
                     * @help for ListBox Binding
                     * @author Anees
                     *
                     * @param 
                     */
                    private static class BindingChangeHandler implements 
                    ChangeHandler{
                                         private T valueModel;
                                         
               public BindingChangeHandler(T valueModel){
                                                             this.valueModel = valueModel;
                                         }
                                         @Override
                                         public void onChange(ChangeEvent event) {
                                                             
                                                             if(event.getSource() instanceof ListBox){
                                                                                 int index = ((ListBox)event.getSource()).getSelectedIndex();
                                                                                 String value = ((ListBox)event.getSource()).getItemText(index);
                                                                                 processListBox(value, valueModel);
                                                             }
                                         }
                    }
                    
                    /**
                     * 
                     * @author Anees
                     *
                     * @param 
                     */
                    private static class BindingValueChangeHandler implements ValueChangeHandler{
 
                                         private T valueModel;
                                         private String caseType;
                                         
                                         public BindingValueChangeHandler(T valueModel, String caseType){
                                                             this.valueModel = valueModel;
                                                             this.caseType = caseType;
                                         }
                                         @Override
                                         public void onValueChange(ValueChangeEvent event) {
                                                             
                                                             if(event.getSource() instanceof TextBox && !(event.getSource() instanceof 
                                                             PasswordTextBox))
                                                                                 sync.processTextBox(event.getValue(), caseType ,valueModel);
                                                             else if(event.getSource() instanceof PasswordTextBox)

                                                                                 sync.processPasswordTextBox(event.getValue(), valueModel);
                                                             
                                                             else if(event.getSource() instanceof RadioButtonWidget)
                                         sync.processRadioButton(event.getValue(),(RadioButtonWidget)event.getSource() 
                                                             ,valueModel);
                                                             
                                                             else if(event.getSource() instanceof CheckBox)
                                                                                 sync.processCheckBox(event.getValue(), valueModel);
                                                             
                                                             else if(event.getSource() instanceof DateBox)
                                                                                 sync.processDateBox(event.getValue(), valueModel);
                                         }
                    }
}
 
Now the moderator class which will synchronize the state of HashMap and Widgets i.e.
GWTWidgetDomainSynchronizer :
 
 
package com.eagle.coders.core.web.gwt.client.ui.bindings;
 
import java.util.List;
 
import com.eagle.coders.core.web.gwt.client.ui.form.widgets.RadioButtonWidget;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
 
/**
 * @author Anees
 *
 */
public class GWTWidgetDomainSynchronizer {
                    
                    private String propertyName;
                    private GWTMapValueModel valueModel;
                    private Widget widget;
                    
                    public GWTWidgetDomainSynchronizer(GWTMapValueModel valueModel, Widget widget){
                                         this.propertyName = valueModel.getKey();
                                         this.valueModel = valueModel;
                                         this.widget = widget;
                                         this.valueModel.createBindableSynch(this);
                                         synchOnBinding();
                    }                                       
                    private void synchOnBinding(){
                                         processGWTMapValueModelChange(null, valueModel.getValue());
                    }                  
                    /**
                     * 
                     * @param valueModel
                     * @param property
                     */
                    public void processGWTMapValueModelChange(Object oldValue, Object newValue){
                                         if(!(widget instanceof PasswordTextBox) && widget instanceof TextBox){
                                                             ((TextBox)widget).setText(newValue.toString());
 
                                         }else if(!(widget instanceof RadioButtonWidget)&& widget instanceof CheckBox){
                                                             CheckBox checkBox = (CheckBox)widget;                                                      
                                                             if(!newValue.equals(checkBox.getValue())){
                                                                                 checkBox.setValue(Boolean.valueOf(newValue.toString()));
                                                             }
                                                                                                                          
                                         }else if(widget instanceof ListBox){                                            
//                                                           TODO: as listbox can have multiple selected data
                                                             if(newValue instanceof List){
                                                                                 
                                                             }
                                                             
                                         }else if(widget instanceof RadioButtonWidget){
                                                             RadioButtonWidget radioButton =(RadioButtonWidget)widget;
                                                             
                                                             if(radioButton.getChoice().equals(newValue)){
                                                                                 
                                                                                 radioButton.setValue(true);
                                                             }else
                                                                                 radioButton.setValue(false);                                                          
                                                                                                                          
                                         }else if(widget instanceof TextArea){
                                                             ((TextArea)widget).setText(newValue.toString());
                                                             
                                         }else if(widget instanceof PasswordTextBox){
                                                             ((PasswordTextBox)widget).setText(newValue.toString());
                                         }
                    }                  
                    /**
                     * 
                     * @param 
                     * @param value
                     * @param valueModel
                     */
                    public  void processTextBox(Object value,String 
                    caseType ,T valueModel ){                                      
                                         String cValue = "";
                                         if(null != caseType){
                                                             if(caseType.equals("upper")){
                                                                                 cValue = value.toString().toUpperCase();                                                                             
                                                             }else if (caseType.equals("lower")){
                                                                                 cValue = value.toString().toLowerCase();
                                                                                 
                                                             }else if (caseType.equals("mixed")){
                                                                                 cValue = value.toString();
                                                             }                                                           
                                                             valueModel.setValue(cValue);
                                              }else {
                                                             valueModel.setValue(value);
                                         }
                    }                  
                    /**
                     * 
                     * @param 
                     * @param value
                     * @param valueModel
                     */
                    public   void processPasswordTextBox(Object value, 
T valueModel ){
                                         valueModel.setValue(value);
                    }                   
                    /**
                     * 
                     * @param 
                     * @param value
                     * @param valueModel
                     */
                    public  void processRadioButton(Object
 value, RadioButtonWidget radioButton ,T valueModel ){
                                         valueModel.setValue(radioButton.getChoice());
                    }
                    
                    /**
                     * 
                     * @param 
                     * @param value
                     * @param valueModel
                     */

                    public  void processCheckBox(Object value, T valueModel 
                    ){
                                         valueModel.setValue(value);
                    }                  
                    /**
                     * 
                     * @param 
                     * @param value
                     * @param valueModel
                     */
                    public  void processListBox(Object value, T valueModel 
                    ){
                                         valueModel.setValue(value);
                    }
                    /**
                     * 
                     * @param 
                     * @param value
                     * @param valueModel
                     */
                    public  void processDateBox(Object value, T valueModel 
                    ){
                                         valueModel.setValue(value);
                    }
}
 
We have tested with all kind of widgets provided by GWT1.6.x+. There is one drawback with this
approach we can use the GWT1.6+ compatible provided widgets as they are based on
new EventModel i.e. Handler's rather than Listeners.

 
 
Example : 
 
    Map map = new HashMap();
    map.put("firstName","");
 
    GWTMapValueModel valueModel = new GWTMapValueModel("firstName", map);
 
    TextBox firstNameWidget = new TextBox();
 
   GWTBinding.bindTextBox(firstNameWidget, valueModel);
 
 
Hopefully this approach will solve many of the binding issues in Google Web Toolkit.