Preserve/render custom data for GridItem

402521: CustomData will not be rendered for items
https://bugs.eclipse.org/bugs/show_bug.cgi?id=402521
diff --git a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/internal/griditemkit/GridItemLCA.java b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/internal/griditemkit/GridItemLCA.java
index 12310a9..3e5d0f9 100644
--- a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/internal/griditemkit/GridItemLCA.java
+++ b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/internal/griditemkit/GridItemLCA.java
@@ -73,6 +73,7 @@
   public void preserveValues( Widget widget ) {
     GridItem item = ( GridItem )widget;
     WidgetLCAUtil.preserveCustomVariant( item );
+    WidgetLCAUtil.preserveData( item );
     preserveProperty( item, PROP_ITEM_COUNT, item.getItemCount() );
     preserveProperty( item, PROP_HEIGHT, item.getHeight() );
     preserveProperty( item, PROP_TEXTS, getTexts( item ) );
@@ -93,6 +94,7 @@
   public void renderChanges( Widget widget ) throws IOException {
     GridItem item = ( GridItem )widget;
     WidgetLCAUtil.renderCustomVariant( item );
+    WidgetLCAUtil.renderData( item );
     renderProperty( item, PROP_ITEM_COUNT, item.getItemCount(), ZERO );
     renderProperty( item, PROP_HEIGHT, item.getHeight(), item.getParent().getItemHeight() );
     renderProperty( item, PROP_TEXTS, getTexts( item ), getDefaultTexts( item ) );
diff --git a/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/internal/griditemkit/GridItemLCA_Test.java b/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/internal/griditemkit/GridItemLCA_Test.java
index f962b2e..9906ae8 100644
--- a/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/internal/griditemkit/GridItemLCA_Test.java
+++ b/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/internal/griditemkit/GridItemLCA_Test.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012 EclipseSource and others.
+ * Copyright (c) 2012, 2013 EclipseSource and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -15,6 +15,8 @@
 import static org.eclipse.nebula.widgets.grid.GridTestUtil.loadImage;
 import static org.eclipse.nebula.widgets.grid.internal.gridkit.GridLCATestUtil.jsonEquals;
 import static org.eclipse.rap.rwt.lifecycle.WidgetUtil.getId;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -27,6 +29,8 @@
 import org.eclipse.nebula.widgets.grid.Grid;
 import org.eclipse.nebula.widgets.grid.GridItem;
 import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.client.Client;
+import org.eclipse.rap.rwt.internal.client.WidgetDataWhiteList;
 import org.eclipse.rap.rwt.internal.protocol.ClientMessageConst;
 import org.eclipse.rap.rwt.lifecycle.WidgetUtil;
 import org.eclipse.rap.rwt.testfixture.Fixture;
@@ -43,6 +47,7 @@
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
+import org.junit.Test;
 
 
 @SuppressWarnings("restriction")
@@ -703,12 +708,48 @@
     assertNotNull( message.findSetOperation( grid.getVerticalBar(), "visibility" ) );
   }
 
+  @Test
+  public void testRenderData() throws JSONException, IOException {
+    fakeWidgetDataWhiteList( new String[]{ "foo", "bar" } );
+    item.setData( "foo", "string" );
+    item.setData( "bar", Integer.valueOf( 1 ) );
+
+    lca.renderChanges( item );
+
+    Message message = Fixture.getProtocolMessage();
+    JSONObject data = ( JSONObject )message.findSetProperty( item, "data" );
+    assertEquals( "string", data.getString( "foo" ) );
+    assertEquals( Integer.valueOf( 1 ), Integer.valueOf( data.getInt( "bar" ) ) );
+  }
+
+  @Test
+  public void testRenderDataUnchanged() throws IOException {
+    fakeWidgetDataWhiteList( new String[]{ "foo" } );
+    item.setData( "foo", "string" );
+    Fixture.markInitialized( display );
+    Fixture.markInitialized( item );
+
+    Fixture.preserveWidgets();
+    lca.renderChanges( item );
+
+    Message message = Fixture.getProtocolMessage();
+    assertEquals( 0, message.getOperationCount() );
+  }
+
   private static void fakeTreeEvent( GridItem item, String eventName ) {
     Map<String, Object> parameters = new HashMap<String, Object>();
     parameters.put( ClientMessageConst.EVENT_PARAM_ITEM, getId( item ) );
     Fixture.fakeNotifyOperation( getId( item.getParent() ), eventName, parameters );
   }
 
+  public static void fakeWidgetDataWhiteList( String[] keys ) {
+    WidgetDataWhiteList service = mock( WidgetDataWhiteList.class );
+    when( service.getKeys() ).thenReturn( keys );
+    Client client = mock( Client.class );
+    when( client.getService( WidgetDataWhiteList.class ) ).thenReturn( service );
+    Fixture.fakeClient( client );
+  }
+
   //////////////////
   // Helping classes