Editing Cells Without Unique Keys in a GWT DataGrid

Imagine that you have a GWT DataGrid to which you would like to add text cell editing capabilities. The actual code is not too hard, the GWT showcase sample code works as advertised out of the box. In this post we will look at a special situation where you might not be able to easily edit cells, and how to work around that.

In the sample, a ProvidesKey is specified to the DataGrid’s SelectionModel so that the DataGrid can tell which row is which for purposes of selecting and editing.

public static final ProvidesKey KEY_PROVIDER = new ProvidesKey() {
   @Override
   public Object getKey(ContactInfo item) {
      return item == null ? null : item.getId();
   }
};

If you don’t specify a ProvidesKey to your DataGrid, the default behavior is to use the row object itself (here, the ContactInfo for each row) as the key. In such a case, each ContactInfo would be compared for identity by using its .equals() method. If ContactInfo.equals() used the ID to determine equality, the behavior would be the same as using the ProvidesKey shown above, and it would be unnecessary to specify this key provider.

However, consider what would happen if ContactInfo didn’t have an ID, and its equals() method did not use reference equality but used data equality so that two rows with the same data would be considered the same. This could happen, for instance, if we added two empty rows and intended to edit them after both were added. For such objects, their .equals() methods would indicate that two row objects are the same when they are actually different. The result in that case is an editor rendered across multiple cells instead of a single cell. This could be resolved by modifying .equals() to use reference equality, but this is not possible if equals() is used by the backing object elsewhere for other intents, or the object is from a third party library.

To resolve this issue, we can wrap such an object with a shallow wrapper that does provide a unique key so that when a ProvidesKey calls .equals() on row objects, rows can correctly be identified as distinct.

public class HasKey {
   private static int nextKey = 0;
   private T row;
   private int key;

   public HasKey(T row) {
      row = row;
      key = nextKey;
      nextKey++;
   }

   public T getRow() {
      return row;
   }

   public int getKey() {
      return key;
   }

   @Override
   public int hashCode() {
      // trivial for brevity, should be based on .key
      return key;
   }

   @Override
   public boolean equals(Object obj) {
      // trivial for brevity, should be based on .key
      final HasKey other = (HasKey) obj;
      return this.key == other.key;
   }
}

If we use this HasKey class as the backing object instead of the original backing object of class T (and forward the appropriate rendering methods to the object it wraps) then we can differentiate backing objects with .equals() as different even when their data is the same. For example, the DataGrid would not be of type T as in DataGrid<T>, it would be of type DataGrid<HasKey<T>>.

Happy editing!

Advertisements

Leave a comment

Filed under Software Engineering

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s