Make key provider a constructor argument to Cell widgets and selection models
Attempt to fix an IndexOutOfBounds and ClassCast exception in ExpenseDetails.java
Fix NPEs in mobile expense app

Review at http://gwt-code-reviews.appspot.com/885801

Review by: iwongu@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8802 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseDetails.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseDetails.java
index 9de125c..b8b0114 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseDetails.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseDetails.java
@@ -388,8 +388,8 @@
   @UiField
   Anchor reportsLink;
 
-  @UiField
-  CellTable<ExpenseProxy> table;
+  @UiField(provided = true)
+  CellTable<ExpenseProxy> table = new CellTable<ExpenseProxy>(Expenses.EXPENSE_RECORD_KEY_PROVIDER);
 
   @UiField
   Element unreconciledLabel;
@@ -456,9 +456,7 @@
   public ExpenseDetails() {
     createErrorPopup();
     initWidget(uiBinder.createAndBindUi(this));
-    items = new ListDataProvider<ExpenseProxy>();
-    items.setKeyProvider(Expenses.EXPENSE_RECORD_KEY_PROVIDER);
-    table.setKeyProvider(items);
+    items = new ListDataProvider<ExpenseProxy>(Expenses.EXPENSE_RECORD_KEY_PROVIDER);
     items.addDataDisplay(table);
 
     // Switch to edit notes.
@@ -584,13 +582,15 @@
 
     // Reset sorting state of table
     lastComparator = defaultComparator;
-    for (SortableHeader header : allHeaders) {
-      header.setSorted(false);
-      header.setReverseSort(true);
+    if (allHeaders.size() > 0) {
+      for (SortableHeader header : allHeaders) {
+        header.setSorted(false);
+        header.setReverseSort(true);
+      }
+      allHeaders.get(0).setSorted(true);
+      allHeaders.get(0).setReverseSort(false);
+      table.redrawHeaders();
     }
-    allHeaders.get(0).setSorted(true);
-    allHeaders.get(0).setReverseSort(false);
-    table.redrawHeaders();
 
     // Request the expenses.
     requestExpenses();
@@ -907,7 +907,9 @@
           Set<SyncResult> syncResults) {
         if (this == lastReceiver) {
           List<ExpenseProxy> list = new ArrayList<ExpenseProxy>(newValues);
-          sortExpenses(list, lastComparator);
+          if (lastComparator != null) {
+            sortExpenses(list, lastComparator);
+          }
           items.setList(list);
           refreshCost();
 
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseList.java
index a089a2f..6cc680e 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseList.java
@@ -58,6 +58,7 @@
 import com.google.gwt.view.client.AsyncDataProvider;
 import com.google.gwt.view.client.HasData;
 import com.google.gwt.view.client.NoSelectionModel;
+import com.google.gwt.view.client.ProvidesKey;
 import com.google.gwt.view.client.Range;
 import com.google.gwt.view.client.SelectionChangeEvent;
 
@@ -175,6 +176,11 @@
    * The data provider used to retrieve reports.
    */
   private class ReportDataProvider extends AsyncDataProvider<ReportProxy> {
+
+    ReportDataProvider(ProvidesKey<ReportProxy> keyProvider) {
+      super(keyProvider);
+    }
+
     @Override
     protected void onRangeChanged(HasData<ReportProxy> display) {
       requestReports(false);
@@ -282,7 +288,7 @@
   /**
    * The data provider that provides reports.
    */
-  private final ReportDataProvider reports = new ReportDataProvider();
+  private final ReportDataProvider reports = new ReportDataProvider(Expenses.REPORT_RECORD_KEY_PROVIDER);
 
   /**
    * The factory used to send requests.
@@ -300,8 +306,6 @@
   private String startsWithSearch;
 
   public ExpenseList() {
-    reports.setKeyProvider(Expenses.REPORT_RECORD_KEY_PROVIDER);
-
     // Initialize the widget.
     createTable();
     searchBox = new DefaultTextBox("search");
@@ -556,6 +560,7 @@
         pager.startLoading();
       }
       lastDataSizeReceiver = new Receiver<Long>() {
+        @Override
         public void onSuccess(Long response, Set<SyncResult> syncResults) {
           if (this == lastDataSizeReceiver) {
             int count = response.intValue();
@@ -570,6 +575,7 @@
 
     // Request reports in the current range.
     lastDataReceiver = new Receiver<List<ReportProxy>>() {
+      @Override
       public void onSuccess(List<ReportProxy> newValues,
           Set<SyncResult> syncResults) {
         if (this == lastDataReceiver) {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseTree.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseTree.java
index b17a43e..3e4b21e 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseTree.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseTree.java
@@ -116,6 +116,7 @@
     private final String department;
 
     public EmployeeListDataProvider(String department) {
+      super(null);
       this.department = department;
     }
 
@@ -126,6 +127,7 @@
       // Request the count anytime a view is added.
       requestFactory.employeeRequest().countEmployeesByDepartment(
           department).fire(new Receiver<Long>() {
+        @Override
         public void onSuccess(Long response, Set<SyncResult> syncResults) {
           updateRowCount(response.intValue(), true);
         }
@@ -229,8 +231,15 @@
   /**
    * The shared {@link SingleSelectionModel}.
    */
-  private final SingleSelectionModel<Object> selectionModel =
-      new SingleSelectionModel<Object>();
+  private final SingleSelectionModel<Object> selectionModel = new SingleSelectionModel<Object>(
+      new ProvidesKey<Object>() {
+        public Object getKey(Object item) {
+          if (item instanceof EmployeeProxy) {
+            return Expenses.EMPLOYEE_RECORD_KEY_PROVIDER.getKey((EmployeeProxy) item);
+          }
+          return item;
+        }
+      });
 
   /**
    * The main widget.
@@ -290,15 +299,6 @@
             }
           }
         });
-    selectionModel.setKeyProvider(new ProvidesKey<Object>() {
-      public Object getKey(Object item) {
-        if (item instanceof EmployeeProxy) {
-          return Expenses.EMPLOYEE_RECORD_KEY_PROVIDER.getKey(
-              (EmployeeProxy) item);
-        }
-        return item;
-      }
-    });
 
     // Create a CellBrowser.
     tree = new CellTree(model, null);
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseDetails.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseDetails.java
index ce3290b..81ed875 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseDetails.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseDetails.java
@@ -70,7 +70,7 @@
             if (expense != null) {
               ExpenseProxy newRecord = event.getProxy();
               if (newRecord.getId().equals(expense.getId())) {
-                show(newRecord);
+                onRefresh(false);
               }
             }
           }
@@ -109,11 +109,10 @@
 
   public void onRefresh(boolean clear) {
     requestFactory.expenseRequest().findExpense(expense.getId()).fire(
-        new Receiver<List<ExpenseProxy>>() {
+        new Receiver<ExpenseProxy>() {
           @Override
-          public void onSuccess(List<ExpenseProxy> response, Set<SyncResult> syncResults) {
-            assert response.size() == 1;
-            show(response.get(0));
+          public void onSuccess(ExpenseProxy response, Set<SyncResult> syncResults) {
+            show(response);
           }
         });
   }
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseEntry.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseEntry.java
index 7ae1bd5..356950b 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseEntry.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseEntry.java
@@ -18,6 +18,7 @@
 import com.google.gwt.core.client.GWT;
 
 import com.google.gwt.dom.client.Element;
+import com.google.gwt.requestfactory.shared.ProxyListRequest;
 import com.google.gwt.requestfactory.shared.Receiver;
 import com.google.gwt.requestfactory.shared.RequestObject;
 import com.google.gwt.requestfactory.shared.SyncResult;
@@ -103,6 +104,7 @@
 
   @SuppressWarnings("deprecation")
   public void onCustom() {
+    requestObject = requestFactory.expenseRequest().persist(expense);
     ExpenseProxy editableExpense = requestObject.edit(expense);
     editableExpense.setDescription(nameText.getText());
     editableExpense.setCategory(categoryText.getText());
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseList.java
index 0ff769e..3d51432 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseList.java
@@ -135,13 +135,12 @@
       final Listener listener, final ExpensesRequestFactory requestFactory) {
     this.listener = listener;
     this.requestFactory = requestFactory;
-    expenseDataProvider = new AsyncDataProvider<ExpenseProxy>() {
+    expenseDataProvider = new AsyncDataProvider<ExpenseProxy>(Expenses.EXPENSE_RECORD_KEY_PROVIDER) {
       @Override
       protected void onRangeChanged(HasData<ExpenseProxy> view) {
         requestExpenses();
       }
     };
-    expenseDataProvider.setKeyProvider(Expenses.EXPENSE_RECORD_KEY_PROVIDER);
 
     expenseList = new CellList<ExpenseProxy>(new ExpenseCell());
 
@@ -159,6 +158,7 @@
     initWidget(expenseList);
   }
 
+  @Override
   public Widget asWidget() {
     return this;
   }
@@ -214,6 +214,7 @@
       return;
     }
     lastReceiver = new Receiver<List<ExpenseProxy>>() {
+      @Override
       public void onSuccess(
           List<ExpenseProxy> newValues, Set<SyncResult> syncResults) {
         if (this == lastReceiver) {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportList.java
index a921747..d1263ed 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportList.java
@@ -65,13 +65,12 @@
     this.requestFactory = requestFactory;
     this.employee = employee;
 
-    reportDataProvider = new AsyncDataProvider<ReportProxy>() {
+    reportDataProvider = new AsyncDataProvider<ReportProxy>(Expenses.REPORT_RECORD_KEY_PROVIDER) {
       @Override
       protected void onRangeChanged(HasData<ReportProxy> view) {
         requestReports();
       }
     };
-    reportDataProvider.setKeyProvider(Expenses.REPORT_RECORD_KEY_PROVIDER);
 
     reportList = new CellList<ReportProxy>(new AbstractCell<ReportProxy>() {
       @Override
@@ -83,8 +82,7 @@
       }
     });
 
-    reportSelection = new NoSelectionModel<ReportProxy>();
-    reportSelection.setKeyProvider(Expenses.REPORT_RECORD_KEY_PROVIDER);
+    reportSelection = new NoSelectionModel<ReportProxy>(Expenses.REPORT_RECORD_KEY_PROVIDER);
     reportSelection.addSelectionChangeHandler(
         new SelectionChangeEvent.Handler() {
           public void onSelectionChange(SelectionChangeEvent event) {
@@ -99,6 +97,7 @@
     onRefresh(false);
   }
 
+  @Override
   public Widget asWidget() {
     return this;
   }
@@ -142,6 +141,7 @@
       return;
     }
     lastReceiver = new Receiver<List<ReportProxy>>() {
+      @Override
       public void onSuccess(
           List<ReportProxy> newValues, Set<SyncResult> syncResults) {
         int size = newValues.size();
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseRequest.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseRequest.java
index 30f6a50..6b1b7d6 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseRequest.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseRequest.java
@@ -16,8 +16,8 @@
 package com.google.gwt.sample.expenses.client.request;
 
 import com.google.gwt.requestfactory.shared.Instance;
-
 import com.google.gwt.requestfactory.shared.ProxyListRequest;
+import com.google.gwt.requestfactory.shared.ProxyRequest;
 import com.google.gwt.requestfactory.shared.RequestObject;
 import com.google.gwt.requestfactory.shared.Service;
 import com.google.gwt.sample.expenses.server.domain.Expense;
@@ -40,7 +40,7 @@
   /**
    * @return a request object
    */
-  ProxyListRequest<ExpenseProxy> findExpense(Long id);
+  ProxyRequest<ExpenseProxy> findExpense(Long id);
 
   /**
    * @return a request object
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Employee.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Employee.java
index c7802b0..bc57930 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Employee.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Employee.java
@@ -78,7 +78,8 @@
     }
     EntityManager em = entityManager();
     try {
-      return em.find(Employee.class, id);
+      Employee employee = em.find(Employee.class, id);
+      return employee;
     } finally {
       em.close();
     }
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Expense.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Expense.java
index 4ca3206..553c25d 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Expense.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Expense.java
@@ -148,7 +148,7 @@
     return this.reasonDenied;
   }
 
-  public Report getReporter() {
+  public Report getReport() {
     return reportId != null ? Report.findReport(reportId) : null;
   }
 
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/ReportBack.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/ReportBack.java
index 5c24d0e..88f90c7 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/ReportBack.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/ReportBack.java
@@ -112,7 +112,7 @@
     EntityManager em = entityManager();
     try {
       List<ReportBack> reportList = em.createQuery("select o from Report o").setFirstResult(
-          0).setMaxResults(maxResults).getResultList();
+          firstResult).setMaxResults(maxResults).getResultList();
       // force it to materialize
       reportList.size();
       return reportList;
@@ -453,6 +453,7 @@
     this.version = version;
   }
 
+  @Override
   public String toString() {
     StringBuilder sb = new StringBuilder();
     sb.append("Id: ").append(getId()).append(", ");
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ContactTreeViewModel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ContactTreeViewModel.java
index e760a7f..3cc742b 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ContactTreeViewModel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ContactTreeViewModel.java
@@ -267,8 +267,7 @@
             ContactDatabase.get().queryContactsByCategoryAndFirstName(count.category,
                 count.firstLetter + "");
       ListDataProvider<ContactInfo> dataProvider = new ListDataProvider<
-          ContactInfo>(contacts);
-      dataProvider.setKeyProvider(ContactInfo.KEY_PROVIDER);
+          ContactInfo>(contacts, ContactInfo.KEY_PROVIDER);
       return new DefaultNodeInfo<ContactInfo>(
           dataProvider, contactCell, selectionModel, null);
     }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellBrowser.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellBrowser.java
index a1e9d33..dd0f537 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellBrowser.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellBrowser.java
@@ -92,8 +92,7 @@
   @ShowcaseSource
   @Override
   public Widget onInitialize() {
-    final MultiSelectionModel<ContactInfo> selectionModel = new MultiSelectionModel<ContactInfo>();
-    selectionModel.setKeyProvider(ContactDatabase.ContactInfo.KEY_PROVIDER);
+    final MultiSelectionModel<ContactInfo> selectionModel = new MultiSelectionModel<ContactInfo>(ContactDatabase.ContactInfo.KEY_PROVIDER);
     selectionModel.addSelectionChangeHandler(
         new SelectionChangeEvent.Handler() {
           public void onSelectionChange(SelectionChangeEvent event) {
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellList.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellList.java
index 0a15c0e..c236738 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellList.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellList.java
@@ -165,11 +165,15 @@
 
     // Create a CellList.
     ContactCell contactCell = new ContactCell(images.contact());
-    cellList = new CellList<ContactInfo>(contactCell);
+
+    // Set a key provider that provides a unique key for each contact. If key is
+    // used to identify contacts when fields (such as the name and address)
+    // change.
+    cellList = new CellList<ContactInfo>(contactCell, ContactDatabase.ContactInfo.KEY_PROVIDER);
     cellList.setPageSize(30);
 
     // Add a selection model so we can select cells.
-    final SingleSelectionModel<ContactInfo> selectionModel = new SingleSelectionModel<ContactInfo>();
+    final SingleSelectionModel<ContactInfo> selectionModel = new SingleSelectionModel<ContactInfo>(ContactDatabase.ContactInfo.KEY_PROVIDER);
     cellList.setSelectionModel(selectionModel);
     selectionModel.addSelectionChangeHandler(
         new SelectionChangeEvent.Handler() {
@@ -178,12 +182,6 @@
           }
         });
 
-    // Set a key provider that provides a unique key for each contact. If key is
-    // used to identify contacts when fields (such as the name and address)
-    // change.
-    cellList.setKeyProvider(ContactDatabase.ContactInfo.KEY_PROVIDER);
-    selectionModel.setKeyProvider(ContactDatabase.ContactInfo.KEY_PROVIDER);
-
     // Add the CellList to the data provider in the database.
     ContactDatabase.get().addDataDisplay(cellList);
 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellSampler.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellSampler.java
index 10ba808..dbc5850 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellSampler.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellSampler.java
@@ -250,8 +250,7 @@
 
     // Create the table.
     editableCells = new ArrayList<AbstractEditableCell<?, ?>>();
-    cellTable = new CellTable<ContactInfo>(6);
-    cellTable.setKeyProvider(ContactInfo.KEY_PROVIDER);
+    cellTable = new CellTable<ContactInfo>(6, ContactInfo.KEY_PROVIDER);
     ContactDatabase.get().addDataDisplay(cellTable);
 
     // CheckboxCell.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellTable.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellTable.java
index e10fdf7..09e0912 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellTable.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellTable.java
@@ -113,7 +113,11 @@
   @Override
   public Widget onInitialize() {
     // Create a CellTable.
-    cellTable = new CellTable<ContactInfo>();
+
+    // Set a key provider that provides a unique key for each contact. If key is
+    // used to identify contacts when fields (such as the name and address)
+    // change.
+    cellTable = new CellTable<ContactInfo>(ContactDatabase.ContactInfo.KEY_PROVIDER);
 
     // Create a Pager to control the table.
     SimplePager.Resources pagerResources = GWT.create(
@@ -123,18 +127,12 @@
     pager.setDisplay(cellTable);
 
     // Add a selection model so we can select cells.
-    final MultiSelectionModel<ContactInfo> selectionModel = new MultiSelectionModel<ContactInfo>();
+    final MultiSelectionModel<ContactInfo> selectionModel = new MultiSelectionModel<ContactInfo>(ContactDatabase.ContactInfo.KEY_PROVIDER);
     cellTable.setSelectionModel(selectionModel);
 
     // Initialize the columns.
     initTableColumns(selectionModel);
 
-    // Set a key provider that provides a unique key for each contact. If key is
-    // used to identify contacts when fields (such as the name and address)
-    // change.
-    cellTable.setKeyProvider(ContactDatabase.ContactInfo.KEY_PROVIDER);
-    selectionModel.setKeyProvider(ContactDatabase.ContactInfo.KEY_PROVIDER);
-
     // Add the CellList to the adapter in the database.
     ContactDatabase.get().addDataDisplay(cellTable);
 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellTree.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellTree.java
index 7f7e01c..8278b44 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellTree.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellTree.java
@@ -91,8 +91,8 @@
   @ShowcaseSource
   @Override
   public Widget onInitialize() {
-    final MultiSelectionModel<ContactInfo> selectionModel = new MultiSelectionModel<ContactInfo>();
-    selectionModel.setKeyProvider(ContactDatabase.ContactInfo.KEY_PROVIDER);
+    final MultiSelectionModel<ContactInfo> selectionModel =
+      new MultiSelectionModel<ContactInfo>(ContactDatabase.ContactInfo.KEY_PROVIDER);
     selectionModel.addSelectionChangeHandler(
         new SelectionChangeEvent.Handler() {
           public void onSelectionChange(SelectionChangeEvent event) {
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellValidation.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellValidation.java
index 6f4b9a0..e3d498b 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellValidation.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellValidation.java
@@ -222,8 +222,7 @@
   @Override
   public Widget onInitialize() {
     // Create a table.
-    final CellTable<ContactInfo> table = new CellTable<ContactInfo>(10);
-    table.setKeyProvider(ContactInfo.KEY_PROVIDER);
+    final CellTable<ContactInfo> table = new CellTable<ContactInfo>(10, ContactInfo.KEY_PROVIDER);
 
     // Add the Name column.
     table.addColumn(new Column<ContactInfo, String>(new TextCell()) {
diff --git a/user/src/com/google/gwt/app/place/AbstractProxyListActivity.java b/user/src/com/google/gwt/app/place/AbstractProxyListActivity.java
index 19d4bcf..2a1dc37 100644
--- a/user/src/com/google/gwt/app/place/AbstractProxyListActivity.java
+++ b/user/src/com/google/gwt/app/place/AbstractProxyListActivity.java
@@ -28,6 +28,7 @@
 import com.google.gwt.user.cellview.client.AbstractHasData;
 import com.google.gwt.user.client.ui.AcceptsOneWidget;
 import com.google.gwt.view.client.HasData;
+import com.google.gwt.view.client.ProvidesKey;
 import com.google.gwt.view.client.Range;
 import com.google.gwt.view.client.RangeChangeEvent;
 import com.google.gwt.view.client.SelectionChangeEvent;
@@ -73,12 +74,6 @@
   private final SingleSelectionModel<P> selectionModel;
   private final Class<P> proxyType;
 
-  /**
-   * Used by the table and its selection model to rely on record id for
-   * equality.
-   */
-  private final EntityProxyKeyProvider<P> keyProvider = new EntityProxyKeyProvider<P>();
-
   private HandlerRegistration rangeChangeHandler;
   private ProxyListView<P> view;
   private AcceptsOneWidget display;
@@ -98,10 +93,10 @@
       }
     });
 
-    selectionModel = new SingleSelectionModel<P>();
-    selectionModel.setKeyProvider(keyProvider);
+    // Inherit the view's key provider
+    ProvidesKey<P> keyProvider = ((AbstractHasData<P>) hasData).getKeyProvider();
+    selectionModel = new SingleSelectionModel<P>(keyProvider);
     hasData.setSelectionModel(selectionModel);
-    ((AbstractHasData<P>) hasData).setKeyProvider(keyProvider);
 
     selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
@@ -137,6 +132,7 @@
     final Range range = listView.getVisibleRange();
 
     final Receiver<List<P>> callback = new Receiver<List<P>>() {
+      @Override
       public void onSuccess(List<P> values, Set<SyncResult> syncResults) {
         if (view == null) {
           // This activity is dead
@@ -236,6 +232,7 @@
 
   private void getLastPage() {
     fireCountRequest(new Receiver<Long>() {
+      @Override
       public void onSuccess(Long response, Set<SyncResult> syncResults) {
         HasData<P> table = getView().asHasData();
         int rows = response.intValue();
@@ -254,6 +251,7 @@
 
   private void init() {
     fireCountRequest(new Receiver<Long>() {
+      @Override
       public void onSuccess(Long response, Set<SyncResult> syncResults) {
         getView().asHasData().setRowCount(response.intValue(), true);
         onRangeChanged(view.asHasData());
@@ -272,6 +270,7 @@
       return;
     }
     fireRangeRequest(new Range(row, 1), new Receiver<List<P>>() {
+      @Override
       public void onSuccess(List<P> response, Set<SyncResult> syncResults) {
         getView().asHasData().setRowData(row,
             Collections.singletonList(response.get(0)));
diff --git a/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java b/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
index ff8cbf6..c5f74e9 100644
--- a/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
+++ b/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
@@ -239,9 +239,10 @@
    *
    * @param pageSize the page size
    */
-  public AbstractHasData(Element elem, final int pageSize) {
+  public AbstractHasData(Element elem, final int pageSize, final ProvidesKey<T> keyProvider) {
     setElement(elem);
     this.presenter = new HasDataPresenter<T>(this, new View<T>(this), pageSize);
+    this.providesKey = keyProvider;
   }
 
   public HandlerRegistration addRangeChangeHandler(
@@ -316,10 +317,6 @@
     presenter.redraw();
   }
 
-  public void setKeyProvider(ProvidesKey<T> keyProvider) {
-    this.providesKey = keyProvider;
-  }
-
   /**
    * Set the number of rows per page and refresh the view.
    *
diff --git a/user/src/com/google/gwt/user/cellview/client/CellBrowser.java b/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
index 6d42949..1bb919a 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
@@ -725,9 +725,9 @@
   // TODO(jlabanca): Move createDisplay into constructor factory arg?
   protected <C> AbstractHasData<C> createDisplay(
       NodeInfo<C> nodeInfo, Cell<C> cell) {
-    CellList<C> display = new CellList<C>(cell, getCellListResources());
+    CellList<C> display = new CellList<C>(cell, getCellListResources(),
+        nodeInfo.getProvidesKey());
     display.setValueUpdater(nodeInfo.getValueUpdater());
-    display.setKeyProvider(nodeInfo.getProvidesKey());
     return display;
   }
 
diff --git a/user/src/com/google/gwt/user/cellview/client/CellList.java b/user/src/com/google/gwt/user/cellview/client/CellList.java
index ff88be8..e1d3793 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellList.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellList.java
@@ -133,7 +133,7 @@
    * @param cell the cell used to render each item
    */
   public CellList(final Cell<T> cell) {
-    this(cell, getDefaultResources());
+    this(cell, getDefaultResources(), null);
   }
 
   /**
@@ -143,7 +143,31 @@
    * @param resources the resources used for this widget
    */
   public CellList(final Cell<T> cell, Resources resources) {
-    super(Document.get().createDivElement(), DEFAULT_PAGE_SIZE);
+    this(cell, resources, null);
+  }
+
+  /**
+   * Construct a new {@link CellList} with the specified {@link ProvidesKey key provider}.
+   * 
+   * @param cell the cell used to render each item
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public CellList(final Cell<T> cell, ProvidesKey<T> keyProvider) {
+    this(cell, getDefaultResources(), keyProvider);
+  }
+
+  /**
+   * Construct a new {@link CellList} with the specified {@link Resources}
+   * and {@link ProvidesKey key provider}.
+   * 
+   * @param cell the cell used to render each item
+   * @param resources the resources used for this widget
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public CellList(final Cell<T> cell, Resources resources, ProvidesKey<T> keyProvider) {
+    super(Document.get().createDivElement(), DEFAULT_PAGE_SIZE, keyProvider);
     this.cell = cell;
     this.style = resources.cellListStyle();
     this.style.ensureInjected();
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTable.java b/user/src/com/google/gwt/user/cellview/client/CellTable.java
index 1998280..80fc682 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTable.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellTable.java
@@ -433,10 +433,20 @@
    * @param pageSize the page size
    */
   public CellTable(final int pageSize) {
-    this(pageSize, getDefaultResources());
+    this(pageSize, getDefaultResources(), null);
   }
 
   /**
+   * Constructs a table with a default page size of 15, and the given {@link ProvidesKey key provider}.
+   * 
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public CellTable(ProvidesKey<T> keyProvider) {
+    this(DEFAULT_PAGESIZE, getDefaultResources(), keyProvider);
+  }
+  
+  /**
    * Constructs a table with the given page size with the specified
    * {@link Resources}.
    * 
@@ -444,7 +454,31 @@
    * @param resources the resources to use for this widget
    */
   public CellTable(final int pageSize, Resources resources) {
-    super(Document.get().createTableElement(), pageSize);
+    this(pageSize, resources, null);
+  }
+
+  /**
+   * Constructs a table with the given page size and the given {@link ProvidesKey key provider}.
+   * 
+   * @param pageSize the page size
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public CellTable(final int pageSize, ProvidesKey<T> keyProvider) {
+    this(pageSize, getDefaultResources(), keyProvider);
+  }
+
+  /**
+   * Constructs a table with the given page size, the specified
+   * {@link Resources}, and the given key provider.
+   * 
+   * @param pageSize the page size
+   * @param resources the resources to use for this widget
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public CellTable(final int pageSize, Resources resources, ProvidesKey<T> keyProvider) {
+    super(Document.get().createTableElement(), pageSize, keyProvider);
     if (TABLE_IMPL == null) {
       TABLE_IMPL = GWT.create(Impl.class);
     }
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java b/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
index 606b90f..6652d33 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
@@ -148,10 +148,10 @@
         }
 
         // Render the child nodes.
-        ProvidesKey<C> providesKey = nodeInfo.getProvidesKey();
+        ProvidesKey<C> keyProvider = nodeInfo.getProvidesKey();
         TreeViewModel model = nodeView.tree.getTreeViewModel();
         for (C value : values) {
-          Object key = providesKey.getKey(value);
+          Object key = keyProvider.getKey(value);
           boolean isOpen = openNodes.contains(key);
 
           // Outer div contains image, value, and children (when open)
@@ -261,7 +261,7 @@
         int len = values.size();
         int end = start + len;
         int childCount = nodeView.getChildCount();
-        ProvidesKey<C> providesKey = nodeInfo.getProvidesKey();
+        ProvidesKey<C> keyProvider = nodeInfo.getProvidesKey();
 
         Element container = nodeView.ensureChildContainer();
         Element childElem = container.getFirstChildElement();
@@ -269,7 +269,7 @@
           C childValue = values.get(i - start);
           CellTreeNodeView<C> child = nodeView.createTreeNodeView(nodeInfo,
               childElem, childValue, null);
-          CellTreeNodeView<?> savedChild = savedViews.remove(providesKey.getKey(childValue));
+          CellTreeNodeView<?> savedChild = savedViews.remove(keyProvider.getKey(childValue));
           // Copy the saved child's state into the new child
           if (savedChild != null) {
             child.animationFrame = savedChild.animationFrame;
@@ -342,12 +342,12 @@
         }
 
         // Trim the saved views down to the children that still exists.
-        ProvidesKey<C> providesKey = nodeInfo.getProvidesKey();
+        ProvidesKey<C> keyProvider = nodeInfo.getProvidesKey();
         Map<Object, CellTreeNodeView<?>> savedViews = new HashMap<Object, CellTreeNodeView<?>>();
         for (C childValue : values) {
           // Remove any child elements that correspond to prior children
           // so the call to setInnerHtml will not destroy them
-          Object key = providesKey.getKey(childValue);
+          Object key = keyProvider.getKey(childValue);
           CellTreeNodeView<?> savedView = openNodes.remove(key);
           if (savedView != null) {
             savedView.ensureAnimationFrame().removeFromParent();
diff --git a/user/src/com/google/gwt/user/cellview/client/Column.java b/user/src/com/google/gwt/user/cellview/client/Column.java
index 89ec9da..b730cd5 100644
--- a/user/src/com/google/gwt/user/cellview/client/Column.java
+++ b/user/src/com/google/gwt/user/cellview/client/Column.java
@@ -54,12 +54,12 @@
   public abstract C getValue(T object);
 
   /**
-   * @param providesKey an instance of ProvidesKey<T>, or null if the record
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
    *          object should act as its own key.
    */
   public void onBrowserEvent(Element elem, final int index, final T object,
-      NativeEvent event, ProvidesKey<T> providesKey) {
-    Object key = getKey(object, providesKey);
+      NativeEvent event, ProvidesKey<T> keyProvider) {
+    Object key = getKey(object, keyProvider);
     ValueUpdater<C> valueUpdater = (fieldUpdater == null)
         ? null : new ValueUpdater<C>() {
           public void update(C value) {
diff --git a/user/src/com/google/gwt/user/client/ui/ValuePicker.java b/user/src/com/google/gwt/user/client/ui/ValuePicker.java
index e8d3169..8115023 100644
--- a/user/src/com/google/gwt/user/client/ui/ValuePicker.java
+++ b/user/src/com/google/gwt/user/client/ui/ValuePicker.java
@@ -91,6 +91,7 @@
   /**
    * @return this view
    */
+  @Override
   public ValuePicker<T> asWidget() {
     return this;
   }
diff --git a/user/src/com/google/gwt/view/client/AbstractDataProvider.java b/user/src/com/google/gwt/view/client/AbstractDataProvider.java
index d79fc1c..1f9c7fd 100644
--- a/user/src/com/google/gwt/view/client/AbstractDataProvider.java
+++ b/user/src/com/google/gwt/view/client/AbstractDataProvider.java
@@ -40,7 +40,7 @@
   /**
    * The provider of keys for list items.
    */
-  private ProvidesKey<T> keyProvider;
+  private final ProvidesKey<T> keyProvider;
 
   /**
    * The last row count.
@@ -57,6 +57,14 @@
    */
   private Map<HasData<T>, HandlerRegistration> rangeChangeHandlers =
       new HashMap<HasData<T>, HandlerRegistration>();
+  
+  protected AbstractDataProvider() {
+    this.keyProvider = null;
+  }
+  
+  protected AbstractDataProvider(ProvidesKey<T> keyProvider) {
+    this.keyProvider = keyProvider;
+  }
 
   /**
    * Adds a data display to this adapter. The current range of interest of the
@@ -148,15 +156,6 @@
   }
 
   /**
-   * Set the {@link ProvidesKey} that provides keys for list items.
-   *
-   * @param keyProvider the {@link ProvidesKey}
-   */
-  public void setKeyProvider(ProvidesKey<T> keyProvider) {
-    this.keyProvider = keyProvider;
-  }
-
-  /**
    * Called when a display changes its range of interest.
    *
    * @param display the display whose range has changed
diff --git a/user/src/com/google/gwt/view/client/AsyncDataProvider.java b/user/src/com/google/gwt/view/client/AsyncDataProvider.java
index 90baace..78fcb9e 100644
--- a/user/src/com/google/gwt/view/client/AsyncDataProvider.java
+++ b/user/src/com/google/gwt/view/client/AsyncDataProvider.java
@@ -29,6 +29,23 @@
  */
 public abstract class AsyncDataProvider<T> extends AbstractDataProvider<T> {
 
+  /**
+   * Constructs an AsyncDataProvider without a key provider.
+   */
+  protected AsyncDataProvider() {
+    super(null);
+  }
+
+  /**
+   * Constructs an AsyncDataProvider with the given key provider.
+   *
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  protected AsyncDataProvider(ProvidesKey<T> keyProvider) {
+    super(keyProvider);
+  }
+
   @Override
   public void updateRowCount(int size, boolean exact) {
     super.updateRowCount(size, exact);
diff --git a/user/src/com/google/gwt/view/client/DefaultSelectionModel.java b/user/src/com/google/gwt/view/client/DefaultSelectionModel.java
index a2b03d3..eaeaad0 100644
--- a/user/src/com/google/gwt/view/client/DefaultSelectionModel.java
+++ b/user/src/com/google/gwt/view/client/DefaultSelectionModel.java
@@ -40,6 +40,23 @@
   private final HashMap<T, Boolean> selectionChanges = new HashMap<T, Boolean>();
 
   /**
+   * Constructs a DefaultSelectionModel without a key provider.
+   */
+  public DefaultSelectionModel() {
+    super(null);
+  }
+  
+  /**
+   * Constructs a DefaultSelectionModel with the given key provider.
+   *
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public DefaultSelectionModel(ProvidesKey<T> keyProvider) {
+    super(keyProvider);
+  }
+  
+  /**
    * Removes all exceptions.
    */
   public void clearExceptions() {
diff --git a/user/src/com/google/gwt/view/client/HasKeyProvider.java b/user/src/com/google/gwt/view/client/HasKeyProvider.java
index e568037..555a13d 100644
--- a/user/src/com/google/gwt/view/client/HasKeyProvider.java
+++ b/user/src/com/google/gwt/view/client/HasKeyProvider.java
@@ -33,14 +33,4 @@
    * @return the {@link ProvidesKey}
    */
   ProvidesKey<T> getKeyProvider();
-
-  /**
-   * Sets the {@link ProvidesKey} instance that will be used to generate keys
-   * for each record object as needed.
-   *
-   * @param keyProvider an instance of {@link ProvidesKey} used to generate keys
-   *          for record objects.
-   */
-  // TODO(jlabanca) - Do we rehash the view data if it changes?
-  void setKeyProvider(ProvidesKey<T> keyProvider);
 }
diff --git a/user/src/com/google/gwt/view/client/ListDataProvider.java b/user/src/com/google/gwt/view/client/ListDataProvider.java
index 6409c46..9674447 100644
--- a/user/src/com/google/gwt/view/client/ListDataProvider.java
+++ b/user/src/com/google/gwt/view/client/ListDataProvider.java
@@ -436,7 +436,7 @@
    * Creates an empty model.
    */
   public ListDataProvider() {
-    this(new ArrayList<T>());
+    this(new ArrayList<T>(), null);
   }
 
   /**
@@ -445,6 +445,29 @@
    * to displays.
    */
   public ListDataProvider(List<T> wrappee) {
+    this(wrappee, null);
+  }
+
+  /**
+   * Creates an empty list model that wraps the given collection.
+   *
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public ListDataProvider(ProvidesKey<T> keyProvider) {
+    this(new ArrayList<T>(), keyProvider);
+  }
+
+  /**
+   * Creates a list model that wraps the given collection. Changes to the
+   * wrapped list must be made via this model in order to be correctly applied
+   * to displays.
+   *
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public ListDataProvider(List<T> wrappee, ProvidesKey<T> keyProvider) {
+    super(keyProvider);
     listWrapper = new ListWrapper(wrappee);
   }
 
diff --git a/user/src/com/google/gwt/view/client/MultiSelectionModel.java b/user/src/com/google/gwt/view/client/MultiSelectionModel.java
index 3ab3885..c24d787 100644
--- a/user/src/com/google/gwt/view/client/MultiSelectionModel.java
+++ b/user/src/com/google/gwt/view/client/MultiSelectionModel.java
@@ -39,6 +39,23 @@
   private final HashMap<T, Boolean> selectionChanges = new HashMap<T, Boolean>();
 
   /**
+   * Constructs a MultiSelectionModel without a key provider.
+   */
+  public MultiSelectionModel() {
+    super(null);
+  }
+  
+  /**
+   * Constructs a MultiSelectionModel with the given key provider.
+   *
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public MultiSelectionModel(ProvidesKey<T> keyProvider) {
+    super(keyProvider);
+  }
+
+  /**
    * Get the set of selected items as a copy.
    *
    * @return the set of selected items
diff --git a/user/src/com/google/gwt/view/client/NoSelectionModel.java b/user/src/com/google/gwt/view/client/NoSelectionModel.java
index 11fe1ac..d29920f 100644
--- a/user/src/com/google/gwt/view/client/NoSelectionModel.java
+++ b/user/src/com/google/gwt/view/client/NoSelectionModel.java
@@ -32,6 +32,23 @@
 
   private Object lastKey;
   private T lastSelection;
+  
+  /**
+   * Constructs a NoSelectionModel without a key provider.
+   */
+  public NoSelectionModel() {
+    super(null);
+  }
+  
+  /**
+   * Constructs a NoSelectionModel with the given key provider.
+   *
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public NoSelectionModel(ProvidesKey<T> keyProvider) {
+    super(keyProvider);
+  }
 
   /**
    * Gets the object that was last selected.
diff --git a/user/src/com/google/gwt/view/client/SelectionModel.java b/user/src/com/google/gwt/view/client/SelectionModel.java
index 2086cb6..06a52f7 100644
--- a/user/src/com/google/gwt/view/client/SelectionModel.java
+++ b/user/src/com/google/gwt/view/client/SelectionModel.java
@@ -53,7 +53,15 @@
      */
     private boolean isEventScheduled;
 
-    private ProvidesKey<T> keyProvider;
+    private final ProvidesKey<T> keyProvider;
+    
+    /**
+     * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+     *        object should act as its own key
+     */
+    protected AbstractSelectionModel(ProvidesKey<T> keyProvider) {
+      this.keyProvider = keyProvider;
+    }
 
     public HandlerRegistration addSelectionChangeHandler(
         SelectionChangeEvent.Handler handler) {
@@ -75,15 +83,6 @@
       return keyProvider;
     }
 
-    /**
-     * Set the key provider for items in this model.
-     * 
-     * @param keyProvider the {@link ProvidesKey}
-     */
-    public void setKeyProvider(ProvidesKey<T> keyProvider) {
-      this.keyProvider = keyProvider;
-    }
-
     protected void fireSelectionChangeEvent() {
       if (isEventScheduled()) {
         setEventCancelled(true);
diff --git a/user/src/com/google/gwt/view/client/SingleSelectionModel.java b/user/src/com/google/gwt/view/client/SingleSelectionModel.java
index d6adc41..bc8fa12 100644
--- a/user/src/com/google/gwt/view/client/SingleSelectionModel.java
+++ b/user/src/com/google/gwt/view/client/SingleSelectionModel.java
@@ -36,6 +36,23 @@
   private T newSelectedObject = null;
 
   /**
+   * Constructs a SingleSelectionModel without a key provider.
+   */
+  public SingleSelectionModel() {
+    super(null);
+  }
+  
+  /**
+   * Constructs a SingleSelectionModel with the given key provider.
+   *
+   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
+   *        object should act as its own key
+   */
+  public SingleSelectionModel(ProvidesKey<T> keyProvider) {
+    super(keyProvider);
+  }
+
+  /**
    * Gets the currently-selected object.
    */
   public T getSelectedObject() {
diff --git a/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java b/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
index 06aa83c..e6eea72 100644
--- a/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
+++ b/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
@@ -304,7 +304,7 @@
     view.assertLastHtml("start=0,size=10");
 
     // Set the selection model.
-    SelectionModel<String> model = new MockSelectionModel<String>();
+    SelectionModel<String> model = new MockSelectionModel<String>(null);
     model.setSelected("test 0", true);
     presenter.setSelectionModel(model);
     view.assertReplaceAllChildrenCalled(true);
@@ -906,7 +906,7 @@
     view.assertLastHtml("start=0,size=10");
 
     // Set the selection model.
-    SelectionModel<String> model = new MockSelectionModel<String>();
+    SelectionModel<String> model = new MockSelectionModel<String>(null);
     model.setSelected("test 0", true);
     presenter.setSelectionModel(model);
     view.assertReplaceAllChildrenCalled(true);
@@ -951,7 +951,7 @@
     view.assertLastHtml("start=0,size=10");
 
     // Set the selection model.
-    SelectionModel<String> model = new MockSelectionModel<String>();
+    SelectionModel<String> model = new MockSelectionModel<String>(null);
     model.setSelected("test 0", true);
     presenter.setSelectionModel(model);
     view.assertReplaceAllChildrenCalled(false);
diff --git a/user/test/com/google/gwt/view/client/AbstractDataProviderTest.java b/user/test/com/google/gwt/view/client/AbstractDataProviderTest.java
index cfcada8..cb500ca 100644
--- a/user/test/com/google/gwt/view/client/AbstractDataProviderTest.java
+++ b/user/test/com/google/gwt/view/client/AbstractDataProviderTest.java
@@ -35,6 +35,10 @@
   static class MockDataProvider<T> extends AbstractDataProvider<T> {
 
     private HasData<T> lastChanged;
+    
+    public MockDataProvider(ProvidesKey<T> keyProvider) {
+      super(keyProvider);
+    }
 
     public void assertLastRangeChanged(HasData<T> expected) {
       assertEquals(expected, lastChanged);
@@ -56,7 +60,7 @@
   }
 
   public void testAddRemoveDataDisplay() {
-    MockDataProvider<String> provider = new MockDataProvider<String>();
+    MockDataProvider<String> provider = new MockDataProvider<String>(null);
 
     // Test with no displays.
     provider.updateRowCount(10, true);
@@ -150,21 +154,20 @@
     assertTrue(displays.contains(display1));
   }
 
-  public void testSetKeyProvider() {
-    AbstractDataProvider<String> provider = createDataProvider();
-
+  public void testKeyProvider() {
     // By default, use the object as a key.
+    AbstractDataProvider<String> provider = createDataProvider(null);
     assertNull(provider.getKeyProvider());
     assertEquals("test", provider.getKey("test"));
     assertEquals(null, provider.getKey(null));
-
-    // Defer to the key provider if one is set.
+    
+    // Set a key provider
     ProvidesKey<String> keyProvider = new ProvidesKey<String>() {
       public Object getKey(String item) {
         return item == null ? item : item.toUpperCase();
       }
     };
-    provider.setKeyProvider(keyProvider);
+    provider = createDataProvider(keyProvider);
     assertEquals(keyProvider, provider.getKeyProvider());
     assertEquals("TEST", provider.getKey("test"));
     assertEquals(null, provider.getKey(null));
@@ -300,7 +303,16 @@
    * @return the data provider
    */
   protected AbstractDataProvider<String> createDataProvider() {
-    return new MockDataProvider<String>();
+    return new MockDataProvider<String>(null);
+  }
+
+  /**
+   * Create an {@link AbstractDataProvider} for testing.
+   *
+   * @return the data provider
+   */
+  protected AbstractDataProvider<String> createDataProvider(ProvidesKey<String> keyProvider) {
+    return new MockDataProvider<String>(keyProvider);
   }
 
   /**
diff --git a/user/test/com/google/gwt/view/client/AbstractSelectionModelTest.java b/user/test/com/google/gwt/view/client/AbstractSelectionModelTest.java
index 73b001b..3b98b65 100644
--- a/user/test/com/google/gwt/view/client/AbstractSelectionModelTest.java
+++ b/user/test/com/google/gwt/view/client/AbstractSelectionModelTest.java
@@ -47,6 +47,11 @@
    * @param <T> the data type
    */
   private static class MockSelectionModel<T> extends AbstractSelectionModel<T> {
+
+    public MockSelectionModel(ProvidesKey<T> keyProvider) {
+      super(keyProvider);
+    }
+    
     public boolean isSelected(T object) {
       return false;
     }
@@ -62,7 +67,7 @@
   }
 
   public void testFireSelectionChangeEvent() {
-    AbstractSelectionModel<String> model = createSelectionModel();
+    AbstractSelectionModel<String> model = createSelectionModel(null);
     MockSelectionChangeHandler handler = new MockSelectionChangeHandler();
     model.addSelectionChangeHandler(handler);
 
@@ -75,7 +80,7 @@
    * Test that resolving changes doesn't prevent an event from firing.
    */
   public void testResolveChanges() {
-    AbstractSelectionModel<String> model = createSelectionModel();
+    AbstractSelectionModel<String> model = createSelectionModel(null);
     final MockSelectionChangeHandler handler = new MockSelectionChangeHandler();
     model.addSelectionChangeHandler(handler);
 
@@ -93,7 +98,7 @@
   }
 
   public void testScheduleSelectionChangeEvent() {
-    AbstractSelectionModel<String> model = createSelectionModel();
+    AbstractSelectionModel<String> model = createSelectionModel(null);
     final MockSelectionChangeHandler handler = new MockSelectionChangeHandler() {
       @Override
       public void onSelectionChange(SelectionChangeEvent event) {
@@ -130,7 +135,7 @@
   }
 
   public void testSetKeyProvider() {
-    AbstractSelectionModel<String> model = createSelectionModel();
+    AbstractSelectionModel<String> model = createSelectionModel(null);
 
     // By default, use the object as a key.
     assertNull(model.getKeyProvider());
@@ -143,13 +148,13 @@
         return item == null ? item : item.toUpperCase();
       }
     };
-    model.setKeyProvider(keyProvider);
+    model = createSelectionModel(keyProvider);
     assertEquals(keyProvider, model.getKeyProvider());
     assertEquals("TEST", model.getKey("test"));
     assertEquals(null, model.getKey(null));
   }
 
-  protected AbstractSelectionModel<String> createSelectionModel() {
-    return new MockSelectionModel<String>();
+  protected AbstractSelectionModel<String> createSelectionModel(ProvidesKey<String> keyProvider) {
+    return new MockSelectionModel<String>(keyProvider);
   }
 }
diff --git a/user/test/com/google/gwt/view/client/DefaultNodeInfoTest.java b/user/test/com/google/gwt/view/client/DefaultNodeInfoTest.java
index d5dbe0f..f828fef 100644
--- a/user/test/com/google/gwt/view/client/DefaultNodeInfoTest.java
+++ b/user/test/com/google/gwt/view/client/DefaultNodeInfoTest.java
@@ -31,7 +31,7 @@
     ListDataProvider<String> provider = new ListDataProvider<String>();
     TextCell cell = new TextCell();
     SingleSelectionModel<String> selectionModel = new SingleSelectionModel<
-        String>();
+        String>(null);
     ValueUpdater<String> valueUpdater = new ValueUpdater<String>() {
       public void update(String value) {
       }
@@ -46,7 +46,7 @@
   }
 
   public void testSetDataDisplay() {
-    MockDataProvider<String> provider = new MockDataProvider<String>();
+    MockDataProvider<String> provider = new MockDataProvider<String>(null);
     DefaultNodeInfo<String> nodeInfo = new DefaultNodeInfo<String>(
         provider, new TextCell());
     MockHasData<String> display = new MockHasData<String>();
diff --git a/user/test/com/google/gwt/view/client/DefaultSelectionModelTest.java b/user/test/com/google/gwt/view/client/DefaultSelectionModelTest.java
index b06691b..dc864ab 100644
--- a/user/test/com/google/gwt/view/client/DefaultSelectionModelTest.java
+++ b/user/test/com/google/gwt/view/client/DefaultSelectionModelTest.java
@@ -29,6 +29,10 @@
    */
   private static class MockDefaultSelectionModel extends
       DefaultSelectionModel<String> {
+    
+    public MockDefaultSelectionModel(ProvidesKey<String> keyProvider) {
+      super(keyProvider);
+    }
 
     @Override
     public boolean isDefaultSelected(String object) {
@@ -37,7 +41,7 @@
   }
 
   public void testIsSelectedWithoutExceptions() {
-    DefaultSelectionModel<String> model = createSelectionModel();
+    DefaultSelectionModel<String> model = createSelectionModel(null);
     assertFalse(model.isSelected(null));
     assertFalse(model.isSelected("test"));
     assertTrue(model.isSelected("selected"));
@@ -45,7 +49,7 @@
   }
 
   public void testSelectedChangeEvent() {
-    DefaultSelectionModel<String> model = createSelectionModel();
+    DefaultSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         finishTest();
@@ -58,7 +62,7 @@
   }
 
   public void testNoDuplicateChangeEvent() {
-    DefaultSelectionModel<String> model = createSelectionModel();
+    DefaultSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         fail();
@@ -73,7 +77,7 @@
 
   public void testSetSelectedDefault() {
     Map<Object, Boolean> exceptions = new HashMap<Object, Boolean>();
-    DefaultSelectionModel<String> model = createSelectionModel();
+    DefaultSelectionModel<String> model = createSelectionModel(null);
     assertTrue(model.isSelected("selected0"));
     assertTrue(model.isSelected("selected1"));
     assertEquals(0, model.getExceptions(exceptions).size());
@@ -96,7 +100,7 @@
   }
 
   public void testSetSelectedNonDefault() {
-    DefaultSelectionModel<String> model = createSelectionModel();
+    DefaultSelectionModel<String> model = createSelectionModel(null);
     assertFalse(model.isSelected("test0"));
     assertFalse(model.isSelected("test1"));
     assertTrue(model.isSelected("selected0"));
@@ -119,13 +123,12 @@
 
   public void testSetSelectedWithKeyProvider() {
     Map<Object, Boolean> exceptions = new HashMap<Object, Boolean>();
-    DefaultSelectionModel<String> model = createSelectionModel();
     ProvidesKey<String> keyProvider = new ProvidesKey<String>() {
       public Object getKey(String item) {
         return item.toUpperCase();
       }
     };
-    model.setKeyProvider(keyProvider);
+    DefaultSelectionModel<String> model = createSelectionModel(keyProvider);
     assertFalse(model.isSelected("test"));
     assertTrue(model.isSelected("selected0"));
     assertFalse(model.isSelected("SELECTED0"));
@@ -168,7 +171,7 @@
   }
 
   @Override
-  protected DefaultSelectionModel<String> createSelectionModel() {
-    return new MockDefaultSelectionModel();
+  protected DefaultSelectionModel<String> createSelectionModel(ProvidesKey<String> keyProvider) {
+    return new MockDefaultSelectionModel(keyProvider);
   }
 }
diff --git a/user/test/com/google/gwt/view/client/MockSelectionModel.java b/user/test/com/google/gwt/view/client/MockSelectionModel.java
index e612db0..77fdbc0 100644
--- a/user/test/com/google/gwt/view/client/MockSelectionModel.java
+++ b/user/test/com/google/gwt/view/client/MockSelectionModel.java
@@ -22,6 +22,10 @@
  * @param <T> the selection type
  */
 public class MockSelectionModel<T> extends MultiSelectionModel<T> {
+  
+  public MockSelectionModel(ProvidesKey<T> keyProvider) {
+    super(keyProvider);
+  }
 
   @Override
   protected void scheduleSelectionChangeEvent() {
diff --git a/user/test/com/google/gwt/view/client/MultiSelectionModelTest.java b/user/test/com/google/gwt/view/client/MultiSelectionModelTest.java
index 38ed544..61f5c28 100644
--- a/user/test/com/google/gwt/view/client/MultiSelectionModelTest.java
+++ b/user/test/com/google/gwt/view/client/MultiSelectionModelTest.java
@@ -24,7 +24,7 @@
 public class MultiSelectionModelTest extends AbstractSelectionModelTest {
 
   public void testGetSelectedSet() {
-    MultiSelectionModel<String> model = createSelectionModel();
+    MultiSelectionModel<String> model = createSelectionModel(null);
     Set<String> selected = new HashSet<String>();
     assertEquals(selected, model.getSelectedSet());
 
@@ -42,7 +42,7 @@
   }
 
   public void testSelectedChangeEvent() {
-    MultiSelectionModel<String> model = createSelectionModel();
+    MultiSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         finishTest();
@@ -55,7 +55,7 @@
   }
   
   public void testNoDuplicateChangeEvent() {
-    MultiSelectionModel<String> model = createSelectionModel();
+    MultiSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         fail();
@@ -69,7 +69,7 @@
   }
   
   public void testNoDuplicateChangeEvent2() {
-    MultiSelectionModel<String> model = createSelectionModel();
+    MultiSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         fail();
@@ -84,7 +84,7 @@
   }
 
   public void testSetSelected() {
-    MultiSelectionModel<String> model = createSelectionModel();
+    MultiSelectionModel<String> model = createSelectionModel(null);
     assertFalse(model.isSelected("test0"));
 
     model.setSelected("test0", true);
@@ -100,13 +100,12 @@
   }
 
   public void testSetSelectedWithKeyProvider() {
-    MultiSelectionModel<String> model = createSelectionModel();
     ProvidesKey<String> keyProvider = new ProvidesKey<String>() {
       public Object getKey(String item) {
         return item.toUpperCase();
       }
     };
-    model.setKeyProvider(keyProvider);
+    MultiSelectionModel<String> model = createSelectionModel(keyProvider);
     assertFalse(model.isSelected("test0"));
 
     model.setSelected("test0", true);
@@ -127,7 +126,7 @@
   }
 
   @Override
-  protected MultiSelectionModel<String> createSelectionModel() {
-    return new MultiSelectionModel<String>();
+  protected MultiSelectionModel<String> createSelectionModel(ProvidesKey<String> keyProvider) {
+    return new MultiSelectionModel<String>(keyProvider);
   }
 }
diff --git a/user/test/com/google/gwt/view/client/NoSelectionModelTest.java b/user/test/com/google/gwt/view/client/NoSelectionModelTest.java
index 6b53224..d3f9fdbe 100644
--- a/user/test/com/google/gwt/view/client/NoSelectionModelTest.java
+++ b/user/test/com/google/gwt/view/client/NoSelectionModelTest.java
@@ -21,7 +21,7 @@
 public class NoSelectionModelTest extends AbstractSelectionModelTest {
 
   public void testGetLastSelectedObject() {
-    NoSelectionModel<String> model = createSelectionModel();
+    NoSelectionModel<String> model = createSelectionModel(null);
     assertNull(model.getLastSelectedObject());
 
     model.setSelected("test", true);
@@ -32,7 +32,7 @@
   }
 
   public void testSelectedChangeEvent() {
-    NoSelectionModel<String> model = createSelectionModel();
+    NoSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         finishTest();
@@ -45,7 +45,7 @@
   }
 
   public void testSetSelected() {
-    NoSelectionModel<String> model = createSelectionModel();
+    NoSelectionModel<String> model = createSelectionModel(null);
     assertFalse(model.isSelected("test0"));
 
     model.setSelected("test0", true);
@@ -56,13 +56,12 @@
   }
 
   public void testSetSelectedWithKeyProvider() {
-    NoSelectionModel<String> model = createSelectionModel();
     ProvidesKey<String> keyProvider = new ProvidesKey<String>() {
       public Object getKey(String item) {
         return item.toUpperCase();
       }
     };
-    model.setKeyProvider(keyProvider);
+    NoSelectionModel<String> model = createSelectionModel(keyProvider);
     assertFalse(model.isSelected("test0"));
 
     model.setSelected("test0", true);
@@ -78,7 +77,7 @@
   }
 
   @Override
-  protected NoSelectionModel<String> createSelectionModel() {
-    return new NoSelectionModel<String>();
+  protected NoSelectionModel<String> createSelectionModel(ProvidesKey<String> keyProvider) {
+    return new NoSelectionModel<String>(keyProvider);
   }
 }
diff --git a/user/test/com/google/gwt/view/client/SingleSelectionModelTest.java b/user/test/com/google/gwt/view/client/SingleSelectionModelTest.java
index 231715b..7682268 100644
--- a/user/test/com/google/gwt/view/client/SingleSelectionModelTest.java
+++ b/user/test/com/google/gwt/view/client/SingleSelectionModelTest.java
@@ -21,7 +21,7 @@
 public class SingleSelectionModelTest extends AbstractSelectionModelTest {
 
   public void testGetSelectedObject() {
-    SingleSelectionModel<String> model = createSelectionModel();
+    SingleSelectionModel<String> model = createSelectionModel(null);
     assertNull(model.getSelectedObject());
 
     model.setSelected("test", true);
@@ -32,7 +32,7 @@
   }
 
   public void testSelectedChangeEvent() {
-    SingleSelectionModel<String> model = createSelectionModel();
+    SingleSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         finishTest();
@@ -45,7 +45,7 @@
   }
   
   public void testNoDuplicateChangeEvent() {
-    SingleSelectionModel<String> model = createSelectionModel();
+    SingleSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         fail();
@@ -59,7 +59,7 @@
   }
   
   public void testNoDuplicateChangeEvent2() {
-    SingleSelectionModel<String> model = createSelectionModel();
+    SingleSelectionModel<String> model = createSelectionModel(null);
     SelectionChangeEvent.Handler handler = new SelectionChangeEvent.Handler() {
       public void onSelectionChange(SelectionChangeEvent event) {
         fail();
@@ -74,7 +74,7 @@
   }
 
   public void testSetSelected() {
-    SingleSelectionModel<String> model = createSelectionModel();
+    SingleSelectionModel<String> model = createSelectionModel(null);
     assertFalse(model.isSelected("test0"));
 
     model.setSelected("test0", true);
@@ -90,13 +90,12 @@
   }
 
   public void testSetSelectedWithKeyProvider() {
-    SingleSelectionModel<String> model = createSelectionModel();
     ProvidesKey<String> keyProvider = new ProvidesKey<String>() {
       public Object getKey(String item) {
         return item.toUpperCase();
       }
     };
-    model.setKeyProvider(keyProvider);
+    SingleSelectionModel<String> model = createSelectionModel(keyProvider);
     assertFalse(model.isSelected("test0"));
 
     model.setSelected("test0", true);
@@ -114,7 +113,7 @@
   }
 
   @Override
-  protected SingleSelectionModel<String> createSelectionModel() {
-    return new SingleSelectionModel<String>();
+  protected SingleSelectionModel<String> createSelectionModel(ProvidesKey<String> keyProvider) {
+    return new SingleSelectionModel<String>(keyProvider);
   }
 }