Work on selection model in Bikeshed and fix some checkstyle stuff while we're at it.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7821 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
index 730427c..21b8320 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
@@ -36,35 +36,42 @@
  */
 // TODO - when can we get rid of a view data object?
 // TODO - should viewData implement some interface? (e.g., with commit/rollback/dispose)
-public abstract class Column<T, C, V> implements HasKey<T> {
+public abstract class Column<T, C, V> {
+
   protected final Cell<C, V> cell;
+
   protected FieldUpdater<T, C, V> fieldUpdater;
+
+  /**
+   * An instance of HasKey<T> that is used to provide a key for a row object
+   * for the purpose of retrieving view data.  A value of null means that
+   * the row object itself will be used as the key.
+   */
+  protected HasKey<T> hasKey;
+
   protected Map<Object, V> viewDataMap = new HashMap<Object, V>();
 
-  public Column(Cell<C, V> cell) {
+  public Column(Cell<C, V> cell, HasKey<T> hasKey) {
     this.cell = cell;
+    this.hasKey = hasKey;
+  }
+
+  public Column(Cell<C, V> cell) {
+    this(cell, null);
   }
 
   public boolean consumesEvents() {
     return cell.consumesEvents();
   }
 
-  /**
-   * Returns a key to be used to associate view data with the given object.
-   * The default implementation simply returns the object.
-   */
-  public Object getKey(T object) {
-    return object;
-  }
-
-  public abstract C getValue(T object);
+  public abstract C getValue(T object, int index);
 
   public void onBrowserEvent(Element elem, final int index, final T object,
       NativeEvent event) {
-    Object key = getKey(object);
+    Object key = hasKey == null ? object : hasKey.getKey(object);
     V viewData = viewDataMap.get(key);
     V newViewData = cell.onBrowserEvent(elem,
-        getValue(object), viewData, event, fieldUpdater == null ? null
+        getValue(object, index), viewData, event, fieldUpdater == null ? null
             : new ValueUpdater<C, V>() {
               public void update(C value, V viewData) {
                 fieldUpdater.update(index, object, value, viewData);
@@ -75,8 +82,8 @@
     }
   }
 
-  public void render(T object, StringBuilder sb) {
-    C value = getValue(object);
+  public void render(T object, int index, StringBuilder sb) {
+    C value = getValue(object, index);
     cell.render(value, viewDataMap.get(object), sb);
   }
 
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/HasKey.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/HasKey.java
index 76eb2cf..d910a52 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/HasKey.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/HasKey.java
@@ -16,20 +16,20 @@
 package com.google.gwt.bikeshed.list.client;
 
 /**
- * An interface for extracting a key from a value.  The extracted key
- * must contain suitable implementations of hashCode() and equals().
+ * An interface for extracting a key from a value. The extracted key must
+ * contain suitable implementations of hashCode() and equals().
  *
  * @param <C> the value type for which keys are to be returned
  */
 public interface HasKey<C> {
 
   /**
-   * Return a key that may be used to identify values that should
-   * be treated as the same in UI views.
+   * Return a key that may be used to identify values that should be treated as
+   * the same in UI views.
    *
    * @param value a value of type C.
    * @return an Object that implements appropriate hashCode() and equals()
-   * methods.
+   *         methods.
    */
   Object getKey(C value);
-}
\ No newline at end of file
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
index 915e404..4d3eaec 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
@@ -21,6 +21,7 @@
 import com.google.gwt.bikeshed.list.shared.ListRegistration;
 import com.google.gwt.bikeshed.list.shared.SelectionModel;
 import com.google.gwt.bikeshed.list.shared.SizeChangeEvent;
+import com.google.gwt.bikeshed.list.shared.SelectionModel.SelectionListener;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.EventTarget;
@@ -62,6 +63,12 @@
   private TableSectionElement tfoot;
   private TableSectionElement thead;
   private int totalSize;
+
+  private SelectionListener listener = new SelectionListener() {
+    public void selectionChanged() {
+      refresh();
+    }
+  };
   
   public PagingTableListView(ListModel<T> listModel, final int pageSize) {
     this.pageSize = pageSize;
@@ -121,6 +128,10 @@
   public int getPage() {
     return curPage;
   }
+  
+  public int getPageSize() {
+    return pageSize;
+  }
 
   public void nextPage() {
     setPage(curPage + 1);
@@ -202,7 +213,11 @@
   }
 
   public void setSelectionModel(SelectionModel<T> selectionModel) {
+    if (this.selectionModel != null) {
+      this.selectionModel.removeListener(listener);
+    }
     this.selectionModel = selectionModel;
+    selectionModel.addListener(listener);
   }
 
   protected void render(int start, int length, List<T> values) {
@@ -213,7 +228,7 @@
     for (int r = start; r < start + length; ++r) {
       TableRowElement row = rows.getItem(r - pageStart);
       T q = values.get(r - start);
-      if (selectionModel != null && selectionModel.isSelected(q)) {
+      if (selectionModel != null && selectionModel.isSelected(q, r)) {
         row.setClassName("pagingTableListView selected");
       } else {
         row.setClassName("pagingTableListView " +
@@ -224,7 +239,7 @@
       for (int c = 0; c < numCols; ++c) {
         TableCellElement cell = row.getCells().getItem(c);
         StringBuilder sb = new StringBuilder();
-        columns.get(c).render(q, sb);
+        columns.get(c).render(q, r, sb);
         cell.setInnerHTML(sb.toString());
 
         // TODO: Really total hack! There's gotta be a better way...
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java b/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java
index 2dfece3..67e1a7c 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java
@@ -15,14 +15,60 @@
  */
 package com.google.gwt.bikeshed.list.shared;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A model for selection within a list.
  * 
  * @param <T> the data type of records in the list
  */
 public interface SelectionModel<T> {
+  
+  /**
+   * A listener who will be updated when the selection changes.
+   */
+  interface SelectionListener {
+    void selectionChanged();
+  }
+  
+  /**
+   * A default implementation of SelectionModel that provides listener
+   * addition and removal.
+   *
+   * @param <T> the data type of records in the list
+   */
+  abstract class DefaultSelectionModel<T> implements SelectionModel<T> {
 
-  boolean isSelected(T object);
+    protected List<SelectionListener> listeners = new ArrayList<SelectionListener>();
+
+    public void addListener(SelectionListener listener) {
+      if (!listeners.contains(listener)) {
+        listeners.add(listener);
+      }
+    }
+    
+    public void removeListener(SelectionListener listener) {
+      if (listeners.contains(listener)) {
+        listeners.remove(listener);
+      }
+    }
+    
+    public void updateListeners() {
+      // Inform the listeners
+      for (SelectionListener listener : listeners) {
+        listener.selectionChanged();
+      }
+    }
+  }
+  
+  void addListener(SelectionListener listener);
+
+  boolean isSelected(T object, int index);
+  
+  void removeListener(SelectionListener listener);
+  
+  void setSelected(int index, boolean selected);
 
   void setSelected(T object, boolean selected);
 }
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java b/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java
index cde957f..ba439b7 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java
@@ -22,24 +22,212 @@
 import com.google.gwt.bikeshed.list.client.Header;
 import com.google.gwt.bikeshed.list.client.PagingTableListView;
 import com.google.gwt.bikeshed.list.shared.ListListModel;
-import com.google.gwt.bikeshed.list.shared.SelectionModel;
+import com.google.gwt.bikeshed.list.shared.SelectionModel.DefaultSelectionModel;
 import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.TextBox;
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
+import java.util.TreeSet;
 
 /**
  * A demo of selection features.
  */
 public class MailSample implements EntryPoint {
 
+  class MailSelectionModel extends DefaultSelectionModel<Message> {
+    private static final int ALL = 0;
+    private static final int NONE = 1;
+    private static final int READ = 2;
+    private static final int SENDER = 3;
+    private static final int SUBJECT = 4;
+    private static final int UNREAD = 5;
+
+    private Set<Integer> minusIdExceptions = new TreeSet<Integer>();
+    private Set<Integer> minusRowExceptions = new TreeSet<Integer>();
+    private Set<Integer> plusIdExceptions = new TreeSet<Integer>();
+    private Set<Integer> plusRowExceptions = new TreeSet<Integer>();
+
+    private String search;
+    private int type = NONE;
+
+    public boolean isSelected(Message object, int index) {
+      // Check row exceptions first
+      if (plusRowExceptions.contains(index)) {
+        return true;
+      }
+      if (minusRowExceptions.contains(index)) {
+        return false;
+      }
+      // Check id exceptions next
+      int id = object.id;
+      if (plusIdExceptions.contains(id)) {
+        return true;
+      }
+      if (minusIdExceptions.contains(id)) {
+        return false;
+      }
+      return isDefaultSelected(object);
+    }
+
+    public void setSearch(String search) {
+      this.search = search.toLowerCase();
+      updateListeners();
+    }
+
+    public void setSelected(int index, boolean selected) {
+      if (!selected) {
+        plusRowExceptions.remove(index);
+        minusRowExceptions.add(index);
+      } else {
+        plusRowExceptions.add(index);
+        minusRowExceptions.remove(index);
+      }
+      updateListeners();
+    }
+
+    public void setSelected(int start, int len, boolean selected) {
+      for (int index = start; index < start + len; index++) {
+        if (!selected) {
+          plusRowExceptions.remove(index);
+          minusRowExceptions.add(index);
+        } else {
+          plusRowExceptions.add(index);
+          minusRowExceptions.remove(index);
+        }
+      }
+      updateListeners();
+    }
+
+    public void setSelected(Message object, boolean selected) {
+      int id = object.id;
+      if (!selected) {
+        plusIdExceptions.remove(id);
+        minusIdExceptions.add(id);
+      } else {
+        plusIdExceptions.add(id);
+        minusIdExceptions.remove(id);
+      }
+      updateListeners();
+    }
+    
+    public void setType(int type) {
+      this.type = type;
+      plusIdExceptions.clear();
+      plusRowExceptions.clear();
+      minusIdExceptions.clear();
+      minusRowExceptions.clear();
+      updateListeners();
+    }
+
+    public String toString() {
+      StringBuilder sb = new StringBuilder();
+      switch (type) {
+        case NONE:
+          sb.append("NONE ");
+          break;
+        case ALL:
+          sb.append("ALL ");
+          break;
+        case READ:
+          sb.append("READ ");
+          break;
+        case UNREAD:
+          sb.append("UNREAD ");
+          break;
+        case SENDER:
+          sb.append("SENDER ");
+          sb.append(search);
+          sb.append(' ');
+          break;
+        case SUBJECT:
+          sb.append("SUBJECT ");
+          sb.append(search);
+          sb.append(' ');
+          break;
+      }
+
+      boolean first = true;
+      for (int i : plusRowExceptions) {
+        if (first) {
+          first = false;
+          sb.append("+row(s) ");
+        }
+        sb.append(i);
+        sb.append(' ');
+      }
+
+      first = true;
+      for (int i : plusIdExceptions) {
+        if (first) {
+          first = false;
+          sb.append("+msg(s) ");
+        }
+        sb.append(i);
+        sb.append(' ');
+      }
+
+      first = true;
+      for (int i : minusRowExceptions) {
+        if (first) {
+          first = false;
+          sb.append("-row(s) ");
+        }
+        sb.append(i);
+        sb.append(' ');
+      }
+
+      first = true;
+      for (int i : minusIdExceptions) {
+        if (first) {
+          first = false;
+          sb.append("-msg(s) ");
+        }
+        sb.append(i);
+        sb.append(' ');
+      }
+
+      return sb.toString();
+    }
+
+    public void updateListeners() {
+      super.updateListeners();
+      selectionLabel.setText("Selected " + this.toString());
+    }
+
+    private boolean isDefaultSelected(Message object) {
+      switch (type) {
+        case NONE:
+          return false;
+        case ALL:
+          return true;
+        case READ:
+          return object.isRead();
+        case UNREAD:
+          return !object.isRead();
+        case SENDER:
+          return object.getSender().toLowerCase().contains(search);
+        case SUBJECT:
+          return object.getSubject().toLowerCase().contains(search);
+        default:
+          throw new IllegalStateException("type = " + type);
+      }
+    }
+  }
+
   class Message {
     int id;
     boolean isRead;
-    boolean isSelected;
     String sender;
     String subject;
 
@@ -79,38 +267,10 @@
       return isRead;
     }
 
-    public boolean isSelected() {
-      return isSelected;
-    }
-
     @Override
     public String toString() {
-      return "Message [id=" + id + ", isSelected=" + isSelected + ", sender="
-          + sender + ", subject=" + subject + ", read=" + isRead + "]";
-    }
-  }
-
-  class MailSelectionModel implements SelectionModel<Message> {
-    private Set<Integer> plusExceptions = new HashSet<Integer>();
-    private Set<Integer> minusExceptions = new HashSet<Integer>();
-
-    private boolean isDefaultSelected(Message object) {
-      return object.isRead();
-    }
-
-    public boolean isSelected(Message object) {
-      return (isDefaultSelected(object) || plusExceptions.contains(object.id))
-          && !minusExceptions.contains(object.id);
-    }
-
-    public void setSelected(Message object, boolean selected) {
-      if (!selected) {
-        minusExceptions.add(object.id);
-        plusExceptions.remove(object.id);
-      } else {
-        minusExceptions.remove(object.id);
-        plusExceptions.add(object.id);
-      }
+      return "Message [id=" + id + ", sender=" + sender + ", subject="
+          + subject + ", read=" + isRead + "]";
     }
   }
 
@@ -120,6 +280,8 @@
   private static final String[] subjects = {
       "GWT rocks", "What's a widget?", "Money in Nigeria"};
 
+  private Label selectionLabel = new Label();
+
   public void onModuleLoad() {
     TextCell textCell = new TextCell();
 
@@ -127,38 +289,38 @@
     List<Message> messages = listModel.getList();
     Random rand = new Random();
     for (int i = 0; i < 1000; i++) {
-      Message message = new Message(i, senders[rand.nextInt(senders.length)],
+      Message message = new Message(10000 + i,
+          senders[rand.nextInt(senders.length)],
           subjects[rand.nextInt(subjects.length)]);
       message.isRead = rand.nextBoolean();
       messages.add(message);
     }
 
-    final SelectionModel<Message> selectionModel = new MailSelectionModel();
-    
-    final PagingTableListView<Message> table =
-      new PagingTableListView<Message>(listModel, 10);
+    final MailSelectionModel selectionModel = new MailSelectionModel();
+
+    final PagingTableListView<Message> table = new PagingTableListView<Message>(
+        listModel, 10);
 
     Column<Message, Boolean, Void> selectedColumn = new Column<Message, Boolean, Void>(
         new CheckboxCell()) {
       @Override
-      public Boolean getValue(Message object) {
-        return selectionModel.isSelected(object);
+      public Boolean getValue(Message object, int index) {
+        return selectionModel.isSelected(object, index);
       }
     };
     selectedColumn.setFieldUpdater(new FieldUpdater<Message, Boolean, Void>() {
       public void update(int index, Message object, Boolean value, Void viewData) {
         selectionModel.setSelected(object, value);
-        table.refresh(); // TODO - remove
       }
     });
     Header<String> selectedHeader = new Header<String>(textCell);
     selectedHeader.setValue("Selected");
     table.addColumn(selectedColumn, selectedHeader);
 
-    Column<Message, String, Void> isReadColumn =
-      new Column<Message, String, Void>(textCell) {
+    Column<Message, String, Void> isReadColumn = new Column<Message, String, Void>(
+        textCell) {
       @Override
-      public String getValue(Message object) {
+      public String getValue(Message object, int index) {
         return object.isRead ? "read" : "unread";
       }
     };
@@ -169,7 +331,7 @@
     Column<Message, String, Void> senderColumn = new Column<Message, String, Void>(
         new TextCell()) {
       @Override
-      public String getValue(Message object) {
+      public String getValue(Message object, int index) {
         return object.getSender();
       }
     };
@@ -180,7 +342,7 @@
     Column<Message, String, Void> subjectColumn = new Column<Message, String, Void>(
         textCell) {
       @Override
-      public String getValue(Message object) {
+      public String getValue(Message object, int index) {
         return object.getSubject();
       }
     };
@@ -189,7 +351,81 @@
     table.addColumn(subjectColumn, subjectHeader);
 
     table.setSelectionModel(selectionModel);
+    
+    Label searchLabel = new Label("Search Sender or Subject:");
+    final TextBox searchBox = new TextBox();
+    searchBox.addKeyUpHandler(new KeyUpHandler() {
+      public void onKeyUp(KeyUpEvent event) {
+        selectionModel.setSearch(searchBox.getText());
+      }
+    });
 
+    Button noneButton = new Button("Select None");
+    Button allOnPageButton = new Button("Select All On This Page");
+    Button allButton = new Button("Select All");
+    Button readButton = new Button("Select Read");
+    Button unreadButton = new Button("Select Unread");
+    Button senderButton = new Button("Search Senders");
+    Button subjectButton = new Button("Search Subject");
+
+    noneButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        selectionModel.setType(MailSelectionModel.NONE);
+      }
+    });
+
+    allOnPageButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        int pageSize = table.getPageSize();
+        selectionModel.setSelected(table.getPage() * pageSize, pageSize, true);
+      }
+    });
+
+    allButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        selectionModel.setType(MailSelectionModel.ALL);
+      }
+    });
+
+    readButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        selectionModel.setType(MailSelectionModel.READ);
+      }
+    });
+
+    unreadButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        selectionModel.setType(MailSelectionModel.UNREAD);
+      }
+    });
+    
+    senderButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        selectionModel.setType(MailSelectionModel.SENDER);
+      }
+    });
+    
+    subjectButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        selectionModel.setType(MailSelectionModel.SUBJECT);
+      }
+    });
+    
+    HorizontalPanel panel = new HorizontalPanel();
+    panel.add(searchLabel);
+    panel.add(searchBox);
+    
+    RootPanel.get().add(panel);
+    RootPanel.get().add(new HTML("<br>"));
     RootPanel.get().add(table);
+    RootPanel.get().add(new HTML("<br>"));
+    RootPanel.get().add(noneButton);
+    RootPanel.get().add(allOnPageButton);
+    RootPanel.get().add(allButton);
+    RootPanel.get().add(readButton);
+    RootPanel.get().add(unreadButton);
+    RootPanel.get().add(subjectButton);
+    RootPanel.get().add(senderButton);
+    RootPanel.get().add(selectionLabel);
   }
 }
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/Columns.java b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/Columns.java
index ae4ed2d..c8b7b06 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/Columns.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/Columns.java
@@ -32,7 +32,7 @@
   static Column<StockQuote, String, Void> buyColumn = new Column<StockQuote, String, Void>(
       new ButtonCell()) {
     @Override
-    public String getValue(StockQuote object) {
+    public String getValue(StockQuote object, int index) {
       return "Buy";
     }
   };
@@ -40,7 +40,7 @@
   static Column<StockQuote, String, Void> changeColumn = new Column<StockQuote, String, Void>(
       new ChangeCell()) {
     @Override
-    public String getValue(StockQuote object) {
+    public String getValue(StockQuote object, int index) {
       return object.getChange();
     }
   };
@@ -48,7 +48,7 @@
   static Column<StockQuote, Integer, Void> dollarsColumn = new Column<StockQuote, Integer, Void>(
       new CurrencyCell()) {
     @Override
-    public Integer getValue(StockQuote object) {
+    public Integer getValue(StockQuote object, int index) {
       return object.getPrice() * object.getSharesOwned();
     }
   };
@@ -56,7 +56,7 @@
   static Column<StockQuote, Boolean, Void> favoriteColumn = new Column<StockQuote, Boolean, Void>(
       new CheckboxCell()) {
     @Override
-    public Boolean getValue(StockQuote object) {
+    public Boolean getValue(StockQuote object, int index) {
       return object.isFavorite();
     }
   };
@@ -67,7 +67,7 @@
   static Column<StockQuote, String, Void> nameColumn = new Column<StockQuote, String, Void>(
       nameCell) {
     @Override
-    public String getValue(StockQuote object) {
+    public String getValue(StockQuote object, int index) {
       return object.getName();
     }
   };
@@ -75,7 +75,7 @@
   static Column<StockQuote, Integer, Void> priceColumn = new Column<StockQuote, Integer, Void>(
       new CurrencyCell()) {
     @Override
-    public Integer getValue(StockQuote object) {
+    public Integer getValue(StockQuote object, int index) {
       return object.getPrice();
     }
   };
@@ -83,7 +83,7 @@
   static Column<StockQuote, Integer, Void> profitLossColumn = new Column<StockQuote, Integer, Void>(
       new ProfitLossCell()) {
     @Override
-    public Integer getValue(StockQuote object) {
+    public Integer getValue(StockQuote object, int index) {
       return object.getValue() - object.getTotalPaid();
     }
   };
@@ -91,7 +91,7 @@
   static Column<StockQuote, String, Void> sellColumn = new Column<StockQuote, String, Void>(
       new ButtonCell()) {
     @Override
-    public String getValue(StockQuote object) {
+    public String getValue(StockQuote object, int index) {
       return "Sell";
     }
   };
@@ -99,7 +99,7 @@
   static Column<StockQuote, String, Void> sharesColumn = new Column<StockQuote, String, Void>(
       new TextCell()) {
     @Override
-    public String getValue(StockQuote object) {
+    public String getValue(StockQuote object, int index) {
       return "" + object.getSharesOwned();
     }
   };
@@ -107,7 +107,7 @@
   static Column<Transaction, String, Void> subtotalColumn = new Column<Transaction, String, Void>(
       new TextCell()) {
     @Override
-    public String getValue(Transaction object) {
+    public String getValue(Transaction object, int index) {
       int price = object.getActualPrice() * object.getQuantity();
       return (object.isBuy() ? " (" : " ")
           + StocksDesktop.getFormattedPrice(price) + (object.isBuy() ? ")" : "");
@@ -117,7 +117,7 @@
   static Column<StockQuote, String, Void> tickerColumn = new Column<StockQuote, String, Void>(
       new TextCell()) {
     @Override
-    public String getValue(StockQuote object) {
+    public String getValue(StockQuote object, int index) {
       return object.getTicker();
     }
   };
@@ -125,7 +125,7 @@
   static Column<Transaction, String, Void> transactionColumn = new Column<Transaction, String, Void>(
       new TextCell()) {
     @Override
-    public String getValue(Transaction object) {
+    public String getValue(Transaction object, int index) {
       return object.toString();
     }
   };
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/validation/client/Validation.java b/bikeshed/src/com/google/gwt/sample/bikeshed/validation/client/Validation.java
index cac83e6..32b9c21 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/validation/client/Validation.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/validation/client/Validation.java
@@ -18,6 +18,7 @@
 import com.google.gwt.bikeshed.cells.client.FieldUpdater;
 import com.google.gwt.bikeshed.cells.client.TextCell;
 import com.google.gwt.bikeshed.list.client.Column;
+import com.google.gwt.bikeshed.list.client.HasKey;
 import com.google.gwt.bikeshed.list.client.PagingTableListView;
 import com.google.gwt.bikeshed.list.shared.ListListModel;
 import com.google.gwt.core.client.EntryPoint;
@@ -85,21 +86,21 @@
     Column<Address, String, Void> stateColumn = new Column<Address, String, Void>(
         new TextCell()) {
       @Override
-      public String getValue(Address object) {
+      public String getValue(Address object, int index) {
         return object.state;
       }
     };
 
-    Column<Address, String, ValidatableField<String>> zipColumn =
-      new Column<Address, String, ValidatableField<String>>(
-        new ValidatableInputCell()) {
-      @Override
+    HasKey<Address> key = new HasKey<Address>() {
       public Object getKey(Address object) {
         return object.key;
       }
-
+    };
+    Column<Address, String, ValidatableField<String>> zipColumn =
+      new Column<Address, String, ValidatableField<String>>(
+        new ValidatableInputCell(), key) {
       @Override
-      public String getValue(Address object) {
+      public String getValue(Address object, int index) {
         return object.zip;
       }
     };
@@ -133,7 +134,7 @@
     Column<Address, String, Void> messageColumn = new Column<Address, String, Void>(
         new TextCell()) {
       @Override
-      public String getValue(Address object) {
+      public String getValue(Address object, int index) {
         return object.zipInvalid ? "Please fix the zip code" : "";
       }
     };
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/shared/ExpensesEntityKey.java b/bikeshed/src/com/google/gwt/sample/expenses/shared/ExpensesEntityKey.java
index 2d036b4..f353db7 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/shared/ExpensesEntityKey.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/shared/ExpensesEntityKey.java
@@ -27,7 +27,7 @@
  * @param <K> this entity type
  */
 public interface ExpensesEntityKey<K extends EntityKey<K>> extends EntityKey<K> {
-  public abstract void accept(ExpensesEntityVisitor visitor);
+  void accept(ExpensesEntityVisitor visitor);
 
-  public abstract <T> T accept(ExpensesEntityFilter<T> filter);
-}
\ No newline at end of file
+  <T> T accept(ExpensesEntityFilter<T> filter);
+}